#!/usr/bin/env python3
"""
phl-engine.py — PHL 실행 엔진 v1.0

PHL(Protocol for Human Language) 토큰을 받아 8단계 실행 사이클을 자동으로 수행한다.
FastMCP 서버로 동작하며, Claude Code 또는 모든 MCP 클라이언트에서 호출 가능.

실행: python3 phl-engine/server.py
등록: .mcp.json → "phl-engine": { "command": "python3", "args": ["phl-engine/server.py"] }

--- 8단계 실행 사이클 ---
① SPEC 로드    → PHL_SPEC.md + PHL_INDEX.json + 토큰 정의 + 계약
② 상태 점검    → 파일 구조/테스트/로그/의존성 확인
③ 계획 보고    → Plan 자동 생성
④ 실행         → FAB 라우트 가동 + Generator 실행
⑤ 검증         → orbit_guard.py + 테스트
⑥ 산출         → boards/ 출력 + CHANGELOG
⑦ 커밋         → 커밋 메시지 자동 생성
⑧ 상태 갱신    → push + PHL 인덱스 업데이트
"""
from __future__ import annotations
from mcp.server.fastmcp import FastMCP
from pydantic import BaseModel, Field
from typing import Optional
import json, os, sys, math, pathlib, subprocess, datetime, textwrap, re, shutil

# ── 경로 ──────────────────────────────────────────────────────────────
HERE = pathlib.Path(__file__).resolve().parent
ROOT = HERE.parent
SPEC_FILE = ROOT / "PHL_SPEC.md"
INDEX_FILE = ROOT / "PHL_INDEX.json"
FAB_FILE = ROOT / "data/fab-manifest.json"
GENERATORS_FILE = ROOT / "data/generators.json"
TOKENS_DIR = ROOT / "phl-spec/tokens"
CONTRACTS_DIR = ROOT / "phl-spec/contracts"
PLAYBOOKS_DIR = ROOT / "phl-spec/playbooks"
GUARD_SCRIPT = ROOT / "scripts/orbit_guard.py"
STATE_FILE = ROOT / "phl-engine/state.json"

mcp = FastMCP("phl-engine")

# ── 데이터 로더 ──────────────────────────────────────────────────────

def _load_json(p: pathlib.Path) -> dict:
    if p.exists():
        return json.loads(p.read_text(encoding="utf-8"))
    return {}

def _save_json(p: pathlib.Path, data: dict) -> None:
    p.write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")

def _load_text(p: pathlib.Path) -> str:
    if p.exists():
        return p.read_text(encoding="utf-8")
    return "(파일 없음)"

# ── 상태 관리 ─────────────────────────────────────────────────────────

def _get_state() -> dict:
    return _load_json(STATE_FILE)

def _set_state(**updates) -> dict:
    state = _get_state()
    state.update(updates)
    state["updated"] = datetime.datetime.now().isoformat()
    _save_json(STATE_FILE, state)
    return state

def _init_state() -> dict:
    if not STATE_FILE.exists():
        _save_json(STATE_FILE, {
            "version": "1.0",
            "engine": "phl-engine",
            "token_runs": 0,
            "last_run": None,
            "history": [],
            "routes_status": {f"RT-{chr(0x3b1+i)}": "idle" for i in range(8)},
            "created": datetime.datetime.now().isoformat(),
            "updated": datetime.datetime.now().isoformat()
        })
    return _get_state()

_init_state()

# ── 토큰 로드 ─────────────────────────────────────────────────────────

def _resolve_token(query: str) -> Optional[dict]:
    """PHL 토큰 찾기: 정식명 / alias / 한글 / 부분일치"""
    q = query.strip()
    q_lower = q.lower()
    index = _load_json(INDEX_FILE)
    tokens = index.get("tokens", {})

    # 1) 정식명 정확 일치 (대소문자 무시)
    for name in tokens:
        if name.lower() == q_lower:
            return {**tokens[name], "name": name}

    # 2) alias 일치
    for name, spec in tokens.items():
        if q_lower in [a.lower() for a in spec.get("aliases", [])]:
            return {**spec, "name": name}
    
    # 3) 부분 일치
    for name, spec in tokens.items():
        if q_lower in name.lower() or any(q_lower in a.lower() for a in spec.get("aliases", [])):
            return {**spec, "name": name}

    # 4) 확장자동검색: "exp" -> PHL-Expansion
    for name in tokens:
        if q_lower in name.lower().replace("phl-", ""):
            return {**tokens[name], "name": name}
    
    return None

# ── FAB 라우트 실행 ──────────────────────────────────────────────────

def _get_route_for_token(token_name: str) -> Optional[dict]:
    fab = _load_json(FAB_FILE)
    routes = fab.get("routes", {})
    for route_id, route in routes.items():
        if token_name in route.get("phl_tokens", []):
            return {**route, "id": route_id}
    return None

def _run_generator(generator_id: str) -> dict:
    """Generator HTML 파일을 열어 실행 (실제로는 Claude Code에게 작업을 위임)"""
    fab = _load_json(FAB_FILE)
    gens = fab.get("generators", {})
    gen = gens.get(generator_id, {})
    gen_file = gen.get("file", "")
    gen_path = ROOT / gen_file if gen_file else None
    
    return {
        "generator": generator_id,
        "file": str(gen_path) if gen_path and gen_path.exists() else None,
        "output_dir": gen.get("output_dir"),
        "output_format": gen.get("output_format"),
        "status": "ready",
        "note": f"Generator '{generator_id}' 준비됨. Claude Code가 실행 필요."
    }

# ── 표준 출력 포맷터 ────────────────────────────────────────────────

def _format_report(token_name: str, specs_loaded: list, plan: list, 
                   changes: list, validation: list, commit_msg: str,
                   notes: list) -> str:
    """PHL 표준 8섹션 출력 형식"""
    lines = []
    sep = "─" * 60
    
    lines.append(f"## PHL Execution Report: {token_name}")
    lines.append(f"**Time:** {datetime.datetime.now().isoformat()}")
    lines.append("")
    
    # ① Loaded Specs
    lines.append(f"### ① Loaded Specs")
    for s in specs_loaded:
        lines.append(f"- {s}")
    lines.append("")
    
    # ③ Plan
    lines.append(f"### ③ Plan")
    for i, p in enumerate(plan, 1):
        lines.append(f"{i}. {p}")
    lines.append("")
    
    # ④ Changes
    lines.append(f"### ④ Changes")
    if changes:
        for c in changes:
            lines.append(f"- {c}")
    else:
        lines.append("- (변경 없음)")
    lines.append("")
    
    # ⑤ Validation
    lines.append(f"### ⑤ Validation")
    for v in validation:
        lines.append(f"- {v}")
    lines.append("")
    
    # ⑦ Commit
    lines.append(f"### ⑦ Commit")
    lines.append(f"```")
    lines.append(commit_msg)
    lines.append(f"```")
    lines.append("")
    
    # Notes
    if notes:
        lines.append(f"### Notes")
        for n in notes:
            lines.append(f"- {n}")
        lines.append("")
    
    lines.append(sep)
    return "\n".join(lines)

# ── 상태 템플릿 ─────────────────────────────────────────────────────

PHL_STATUS_TEMPLATE = """## PHL Engine Status

**Version:** {version}
**Engine:** {engine}
**Uptime:** {uptime}
**Total Token Runs:** {token_runs}

### Routes Status
{routes_status}

### Last Run
{last_run}

### Registered Tokens
{tokens_list}

*Updated: {updated}*
"""

# ─────────────────────────────────────────────────────────────────────
# MCP 도구
# ─────────────────────────────────────────────────────────────────────

@mcp.tool()
def run_phl_token(token: str, scope: str = "", params: str = "") -> str:
    """
    PHL 토큰을 실행한다. 8단계 실행 사이클을 자동 수행.
    
    Args:
        token: PHL 토큰명 또는 alias (예: "PHL-Expansion", "확장", "hardening")
        scope: 적용 범위 (모듈/파일/디렉토리 경로, 없으면 전체)
        params: 추가 파라미터 (JSON 문자열, 선택)
    
    Returns:
        PHL 표준 8섹션 실행 보고서
    """
    # 1) 토큰 해석
    resolved = _resolve_token(token)
    if not resolved:
        # 등록되지 않은 토큰 → 새로 정의할지 질문
        return f"""## ❌ Unknown Token: '{token}'

정의되지 않은 토큰입니다. 다음 중 선택:
1. `phl_define_token` 도구로 새 토큰 정의
2. `phl_list_tokens`로 등록된 토큰 확인
3. PHL_SPEC.md 섹션 5 스키마에 따라 직접 정의

참고: PHL_SPEC.md → 토큰 스펙 스키마 참조
"""
    
    token_name = resolved["name"]
    token_file = resolved.get("file", "")
    intent = resolved.get("intent", "")
    route_id = resolved.get("route", "")
    
    # 2) 상태 기록
    state = _get_state()
    run_count = state.get("token_runs", 0) + 1
    history = state.get("history", [])
    
    specs_loaded = []
    plan = []
    changes = []
    validation = []
    notes = []
    
    # ════════════════════════════════════════════════════════════════
    # ① SPEC 로드
    # ════════════════════════════════════════════════════════════════
    specs_loaded.append(f"📄 PHL_SPEC.md ({_load_text(SPEC_FILE)[:50]}...)" if SPEC_FILE.exists() else "❌ PHL_SPEC.md 없음")
    if INDEX_FILE.exists():
        idx = _load_json(INDEX_FILE)
        specs_loaded.append(f"📄 PHL_INDEX.json ({len(idx.get('tokens',{}))}개 토큰)")
    
    # 토큰 정의 로드
    token_def_path = ROOT / token_file.lstrip("/") if token_file else TOKENS_DIR / f"{token_name}.md"
    if token_def_path.exists():
        specs_loaded.append(f"📄 {token_name} 정의 ({token_def_path})")
    else:
        specs_loaded.append(f"⚠️ {token_name} 정의 파일 없음 → PHL_INDEX.json에서 인라인 사용")
    
    # 계약 로드
    contracts = _load_json(INDEX_FILE).get("contracts", {})
    for cname, cpath in contracts.items():
        cp = ROOT / cpath.lstrip("/")
        if cp.exists():
            specs_loaded.append(f"📄 Contract: {cname} ({cp})")
    
    # ════════════════════════════════════════════════════════════════
    # ② 상태 점검
    # ════════════════════════════════════════════════════════════════
    # 현재 브랜치
    try:
        branch = subprocess.check_output(
            ["git", "-C", str(ROOT), "branch", "--show-current"],
            stderr=subprocess.DEVNULL, text=True
        ).strip()
    except:
        branch = "unknown"
    specs_loaded.append(f"🌿 Branch: {branch}")
    
    # WIP 체크
    try:
        wip = subprocess.check_output(
            ["git", "-C", str(ROOT), "status", "--porcelain"],
            stderr=subprocess.DEVNULL, text=True
        ).strip()
        if wip:
            wip_count = len([l for l in wip.split('\n') if l.strip()])
            specs_loaded.append(f"⚠️ WIP: {wip_count}개 미커밋 파일")
    except:
        pass
    
    # ════════════════════════════════════════════════════════════════
    # ③ 계획 보고
    # ════════════════════════════════════════════════════════════════
    plan.append(f"[{token_name}] {intent}")
    plan.append(f"범위: {scope if scope else '전체 레포'}")
    plan.append(f"FAB 라우트: {route_id if route_id else '없음'}")
    
    # 라우트-제너레이터 연결
    route = _get_route_for_token(token_name)
    if route:
        gens = route.get("generators", [])
        if gens:
            for g in gens:
                plan.append(f"Generator 실행: {g} → {route.get('output_repos', ['?'])}")
    
    # 토큰별 추가 액션
    if token_name == "PHL-Expansion":
        plan.append("1. 에러 처리 강화 (try/catch, Result 타입)")
        plan.append("2. 로깅 추가 (진입/종료/에러)")
        plan.append("3. 인터페이스 정리 (SRP, 매개변수 ≤3)")
        plan.append("4. 테스트 추가 (happy/error/boundary)")
    elif token_name == "PHL-Hardening":
        plan.append("1. 보안 취약점 스캔")
        plan.append("2. 입력 검증 강화")
        plan.append("3. 비밀키/환경변수 분리 확인")
        plan.append("4. OWASP Top 10 체크")
    elif token_name == "PHL-Reverse":
        plan.append("1. 역방향 검증: 출력→입력 복원")
        plan.append("2. 일관성 체크 (명세 vs 구현)")
        plan.append("3. 역로깅: 실행 역추적 경로 확인")
    
    # ════════════════════════════════════════════════════════════════
    # ④ 실행 (실제 Generator 호출 준비)
    # ════════════════════════════════════════════════════════════════
    if route:
        gens = route.get("generators", [])
        for g in gens:
            result = _run_generator(g)
            changes.append(f"🎯 Generator '{g}' 준비: {result.get('file', 'N/A')}")
            if route.get("studio_tools"):
                for tool in route["studio_tools"]:
                    changes.append(f"🔧 Studio tool: {tool}")
    
    # FAB 라우트 상태 업데이트
    routes_status = state.get("routes_status", {})
    if route_id:
        routes_status[route_id] = f"running (토큰: {token_name})"
    _set_state(routes_status=routes_status)
    
    # ════════════════════════════════════════════════════════════════
    # ⑤ 검증
    # ════════════════════════════════════════════════════════════════
    # orbit_guard.py 실행
    if GUARD_SCRIPT.exists():
        try:
            result = subprocess.run(
                [sys.executable, str(GUARD_SCRIPT)],
                cwd=str(ROOT), capture_output=True, text=True, timeout=30
            )
            if result.returncode == 0:
                validation.append("✅ orbit_guard.py PASSED")
            else:
                validation.append(f"⚠️ orbit_guard.py FAILED:\n{result.stdout}")
        except subprocess.TimeoutExpired:
            validation.append("⏰ orbit_guard.py timeout (30s)")
        except Exception as e:
            validation.append(f"❌ orbit_guard.py error: {e}")
    else:
        validation.append("ℹ️ orbit_guard.py 없음 → 수동 검증 필요")
    
    # ════════════════════════════════════════════════════════════════
    # ⑥ 산출
    # ════════════════════════════════════════════════════════════════
    # boards/ 디렉토리 출력물 확인
    boards_dir = ROOT / "boards"
    if boards_dir.exists():
        board_files = list(boards_dir.glob("*.html"))
        changes.append(f"📊 Boards: {len(board_files)}개 출력물")
    
    # ════════════════════════════════════════════════════════════════
    # ⑦ 커밋 메시지
    # ════════════════════════════════════════════════════════════════
    commit_msg = f"""PHL-{token_name}: {scope if scope else '레포'} - {intent}

- 토큰: {token_name}
- 범위: {scope if scope else '전체'}
- 라우트: {route_id if route_id else 'N/A'}
- 실행: {datetime.datetime.now().isoformat()}

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>"""
    
    # ════════════════════════════════════════════════════════════════
    # ⑧ 상태 갱신
    # ════════════════════════════════════════════════════════════════
    history.append({
        "token": token_name,
        "timestamp": datetime.datetime.now().isoformat(),
        "scope": scope,
        "route": route_id,
        "intent": intent
    })
    # 최근 50개만 유지
    if len(history) > 50:
        history = history[-50:]
    
    if route_id:
        routes_status[route_id] = "idle"
    
    _set_state(
        token_runs=run_count,
        last_run={
            "token": token_name,
            "timestamp": datetime.datetime.now().isoformat(),
            "scope": scope
        },
        history=history,
        routes_status=routes_status
    )
    notes.append(f"🔄 총 실행 횟수: {run_count}")
    notes.append(f"📋 히스토리: {len(history)}개 기록")
    if not route:
        notes.append("ℹ️ 이 토큰은 FAB 라우트에 연결되지 않음 → 수동 실행 필요")
    
    return _format_report(token_name, specs_loaded, plan, changes, 
                         validation, commit_msg, notes)


@mcp.tool()
def phl_status() -> str:
    """
    PHL 엔진 상태를 반환한다. 등록된 토큰, 라우트 상태, 실행 이력 확인.
    """
    state = _get_state()
    index = _load_json(INDEX_FILE)
    tokens = index.get("tokens", {})
    
    now = datetime.datetime.now()
    created = state.get("created", now.isoformat())
    try:
        created_dt = datetime.datetime.fromisoformat(created)
        uptime = str(now - created_dt).split(".")[0]
    except:
        uptime = "unknown"
    
    # 라우트 상태
    rs = state.get("routes_status", {})
    routes_lines = []
    for rid, rstatus in sorted(rs.items()):
        icon = "🟢" if rstatus == "idle" else "🟡" if "running" in rstatus else "⚪"
        routes_lines.append(f"  {icon} {rid}: {rstatus}")
    
    # 토큰 목록
    tokens_lines = []
    for name, spec in sorted(tokens.items()):
        intent = spec.get("intent", "")
        route = spec.get("route", "")
        aliases = ", ".join(spec.get("aliases", [])[:3])
        tokens_lines.append(f"  • **{name}**: {intent}")
        if route:
            tokens_lines.append(f"    → 라우트: {route} | 호출: {aliases}")
    
    last = state.get("last_run", {})
    last_str = f"{last.get('token', 'N/A')} at {last.get('timestamp', 'N/A')}" if last else "없음"
    
    return PHL_STATUS_TEMPLATE.format(
        version=state.get("version", "1.0"),
        engine=state.get("engine", "phl-engine"),
        uptime=uptime,
        token_runs=state.get("token_runs", 0),
        routes_status="\n".join(routes_lines),
        last_run=last_str,
        tokens_list="\n".join(tokens_lines) if tokens_lines else "  (등록된 토큰 없음)",
        updated=state.get("updated", now.isoformat())
    )


@mcp.tool()
def phl_plan(token: str, scope: str = "") -> str:
    """
    PHL 토큰 실행 계획만 미리 보고 (실행하지 않음).
    
    Args:
        token: PHL 토큰명 또는 alias
        scope: 적용 범위
    
    Returns:
        실행 전 계획서
    """
    resolved = _resolve_token(token)
    if not resolved:
        return f"❌ 알 수 없는 토큰: '{token}'. `phl_list_tokens`로 확인."
    
    token_name = resolved["name"]
    intent = resolved.get("intent", "")
    route_id = resolved.get("route", "")
    route = _get_route_for_token(token_name)
    
    lines = []
    lines.append(f"## PHL Plan Preview: {token_name}")
    lines.append(f"**Intent:** {intent}")
    lines.append(f"**Scope:** {scope if scope else '전체 레포'}")
    lines.append(f"**FAB Route:** {route_id if route_id else '없음'}")
    lines.append("")
    lines.append("### Loaded Specs")
    lines.append(f"- PHL_SPEC.md")
    lines.append(f"- PHL_INDEX.json")
    lines.append(f"- {token_name} token definition")
    lines.append(f"- 5 contracts (error/logging/test/security/commit)")
    lines.append("")
    
    if route:
        gens = route.get("generators", [])
        if gens:
            lines.append("### Generators to Run")
            for g in gens:
                lines.append(f"- {g}")
        tools = route.get("studio_tools", [])
        if tools:
            lines.append("### Studio Tools")
            for t in tools:
                lines.append(f"- {t}")
        repos = route.get("output_repos", [])
        if repos:
            lines.append("### Output Repos")
            for r in repos:
                lines.append(f"- {r}")
    
    lines.append("")
    lines.append("### Validation Steps")
    lines.append("- orbit_guard.py 실행")
    if token_name == "PHL-Expansion":
        lines.append("- 에러 처리 / 로깅 / 인터페이스 / 테스트 체크")
    elif token_name == "PHL-Hardening":
        lines.append("- 보안 취약점 / 입력 검증 / 비밀키 / OWASP 체크")
    elif token_name == "PHL-Reverse":
        lines.append("- 역방향 검증 / 일관성 / 역로깅 체크")
    
    lines.append("")
    lines.append("> 실행하려면: `phl_run_token(token='%s', scope='%s')`" % (token_name, scope))
    
    return "\n".join(lines)


@mcp.tool()
def phl_list_tokens() -> str:
    """등록된 모든 PHL 토큰과 alias를 반환한다."""
    index = _load_json(INDEX_FILE)
    tokens = index.get("tokens", {})
    contracts = index.get("contracts", {})
    playbooks = index.get("playbooks", {})
    
    lines = []
    lines.append("## 📋 PHL Token Registry")
    lines.append("")
    lines.append(f"**Spec:** PHL_SPEC.md | **Version:** {index.get('version', '?')}")
    lines.append("")
    
    for name, spec in sorted(tokens.items()):
        aliases = ", ".join(f"`{a}`" for a in spec.get("aliases", []))
        route = spec.get("route", "—")
        cat = spec.get("category", "—")
        lines.append(f"### {name}")
        lines.append(f"- Intent: {spec.get('intent', '')}")
        lines.append(f"- Category: {cat} | Route: {route}")
        lines.append(f"- Aliases: {aliases}")
        lines.append("")
    
    lines.append("### Contracts")
    for cname, cpath in contracts.items():
        lines.append(f"- {cname}: `{cpath}`")
    lines.append("")
    lines.append("### Playbooks")
    for pname, ppath in playbooks.items():
        lines.append(f"- {pname}: `{ppath}`")
    
    return "\n".join(lines)


@mcp.tool()
def phl_define_token(name: str, intent: str, aliases: list,
                     category: str = "general", route: str = "",
                     scope: str = "", non_goals: list = None,
                     preconditions: list = None) -> str:
    """
    새 PHL 토큰을 정의한다. PHL_SPEC.md 스키마에 따라 파일 생성 + 인덱스 등록.
    
    Args:
        name: 토큰명 (예: "PHL-MyToken")
        intent: 1문장 목적
        aliases: 호출어 리스트 (예: ["mytoken", "마이토큰"])
        category: 카테고리 (engineering/security/validation/general)
        route: 연결할 FAB 라우트 (예: "RT-α")
        scope: 기본 적용 범위
        non_goals: 하지 말아야 할 것 리스트
        preconditions: 선행 조건 리스트
    
    Returns:
        생성 결과 보고서
    """
    # 검증
    if not name.startswith("PHL-"):
        return f"❌ 토큰명은 'PHL-'로 시작해야 합니다. (입력: '{name}')"
    
    safe_name = name.replace("PHL-", "")
    token_file = TOKENS_DIR / f"{name}.md"
    
    if token_file.exists():
        return f"⚠️ 토큰 '{name}'이 이미 존재합니다: {token_file}\n수정하려면 직접 편집하세요."
    
    # 토큰 정의 파일 생성
    content = f"""# {name}

## Token Spec

```yaml
Name:           {name}
Intent:         {intent}
Scope:          {scope if scope else '사용자 지정'}
Non-Goals:{'' if non_goals else ' 없음'}
"""
    if non_goals:
        for ng in non_goals:
            content += f"  - {ng}\n"
    
    content += f"""Preconditions:{'' if preconditions else ' 없음'}
"""
    if preconditions:
        for pc in preconditions:
            content += f"  - {pc}\n"
    
    content += f"""```

---

## Procedure (단계별 수행)

### Step 1: 현재 상태 파악
- [ ] 대상 범위 파일 구조 읽기
- [ ] 기존 패턴 파악

### Step 2: 실행
_이 섹션은 토큰 목적에 맞게 작성 필요_

### Step 3: 검증
- [ ] 정상 동작 확인

---

## Validation

- [ ] 기본 검증 통과

---

## Artifacts

| 유형 | 설명 |
|------|------|
| TBD | 정의 필요 |

---

## Commit Policy

```
{name}: <대상> - <요약>

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
```

---

## Rollback Policy

- 실패 시: `git revert`

---

## Questions Policy

- 불확실하면 질문
"""

    token_file.write_text(content, encoding="utf-8")
    
    # 인덱스 업데이트
    index = _load_json(INDEX_FILE)
    tokens = index.get("tokens", {})
    
    alias_list = list(dict.fromkeys([name.lower()] + [a.lower() for a in aliases]))
    
    tokens[name] = {
        "file": f"/phl-spec/tokens/{name}.md",
        "intent": intent,
        "aliases": alias_list,
        "category": category,
        "route": route,
        "er_cmd": f"fab-run.sh --er --token={name}" if route else ""
    }
    
    index["tokens"] = tokens
    _save_json(INDEX_FILE, index)
    
    return f"""## ✅ Token Defined: {name}

### Created Files
- 📄 `phl-spec/tokens/{name}.md` (토큰 정의)
- 📄 PHL_INDEX.json → `tokens.{name}` (인덱스 등록)

### Details
- Intent: {intent}
- Aliases: {', '.join(alias_list)}
- Category: {category}
- Route: {route if route else '연결 안 됨'}

### Usage
```
phl_run_token(token="{name}")
# 또는 alias로: "{alias_list[0]}"
```
"""


@mcp.tool()
def phl_detect(text: str) -> str:
    """
    입력 텍스트에서 PHL 토큰 호출을 자동 감지한다.
    STT 입력에서 "확장", "보강", "하드닝" 등을 감지해 해당 토큰 실행 제안.
    
    Args:
        text: 사용자 입력 텍스트 (STT 또는 채팅)
    
    Returns:
        감지된 토큰 정보
    """
    index = _load_json(INDEX_FILE)
    tokens = index.get("tokens", {})
    detected = []
    
    text_lower = text.lower()
    
    for name, spec in tokens.items():
        aliases = [a.lower() for a in spec.get("aliases", [])]
        aliases.append(name.lower())
        
        for alias in aliases:
            if alias in text_lower:
                detected.append({
                    "name": name,
                    "intent": spec.get("intent", ""),
                    "trigger": alias,
                    "confidence": "high" if alias == name.lower() else "medium"
                })
                break
    
    if not detected:
        return f"⚠️ 감지된 PHL 토큰 없음.\n등록된 토큰: {', '.join(tokens.keys()) if tokens else '없음'}\n`phl_list_tokens()`로 확인."
    
    lines = ["## 🎯 PHL Token Detected", ""]
    for d in detected:
        lines.append(f"### {d['name']} ({d['confidence']})")
        lines.append(f"- Intent: {d['intent']}")
        lines.append(f"- Trigger: '{d['trigger']}'")
        lines.append(f"- 실행: `phl_run_token(token=\"{d['name']}\")`")
        lines.append("")
    
    return "\n".join(lines)

# ─────────────────────────────────────────────────────────────────────
# 리소스
# ─────────────────────────────────────────────────────────────────────

@mcp.resource("phl://state")
def resource_phl_state() -> str:
    """PHL 엔진 현재 상태"""
    return phl_status()

@mcp.resource("phl://tokens")
def resource_phl_tokens() -> str:
    """등록된 모든 PHL 토큰"""
    return phl_list_tokens()

@mcp.resource("phl://spec")
def resource_phl_spec() -> str:
    """PHL_SPEC.md 전체 내용"""
    return _load_text(SPEC_FILE)

@mcp.resource("phl://fab")
def resource_phl_fab() -> str:
    """FAB 매니페스트 전체"""
    return json.dumps(_load_json(FAB_FILE), ensure_ascii=False, indent=2)

# ─────────────────────────────────────────────────────────────────────
# 메인
# ─────────────────────────────────────────────────────────────────────

if __name__ == "__main__":
    print("🧠 PHL Engine v1.0 starting...", file=sys.stderr)
    print(f"   Root: {ROOT}", file=sys.stderr)
    print(f"   Tokens: {len(_load_json(INDEX_FILE).get('tokens', {}))}개 등록", file=sys.stderr)
    print(f"   Routes: {len(_load_json(FAB_FILE).get('routes', {}))}개", file=sys.stderr)
    print(f"   State: {STATE_FILE}", file=sys.stderr)
    mcp.run()
