"""
gdep.init_context
gdep init / gdep context 커맨드 구현.

- gdep init  <path>    : .gdep/AGENTS.md 생성 (AI Agent용 프로젝트 컨텍스트)
- gdep context <path>  : 현재 프로젝트 컨텍스트를 stdout으로 출력
"""
from __future__ import annotations

from datetime import datetime
from pathlib import Path

from . import runner
from .detector import ProjectKind, detect

# ─────────────────────────────────────────────────────────────
# AGENTS.md 최상단 — 모든 AI 클라이언트에 공통으로 적용되는 지시문
# ─────────────────────────────────────────────────────────────

_PREAMBLE = """\
<!--
  This file is auto-generated by `gdep init`.
  It is read automatically by Claude Desktop, Cursor, Claude Code,
  Gemini CLI, and any AI agent that respects AGENTS.md / .cursorrules
  conventions. Do not delete — regenerate with `gdep init --force`.
-->

# gdep Agent Context
> Generated: {date}  |  Engine: {engine}  |  Project: {name}

## Project Analysis Tools

This project uses **gdep MCP** for codebase analysis.
gdep is a static analyser that understands game engine internals:
prefab bindings, Blueprint↔C++ bridges, GAS flows, Animator state machines, etc.

### Available capabilities

1. **gdep tools provide cached, fast structural analysis** — they understand
   game-engine semantics (Blueprint↔C++ bridges, GAS flows, Animator state machines).
   Consider calling `get_project_context` for a cached structural overview at the start of a new session.

2. **Check the Confidence level in every gdep response before acting on it:**
   - **HIGH** (C++ / Roslyn source parsing) — trust without additional verification
   - **MEDIUM** (binary pattern match + cross-reference) — reliable for most queries;
     cross-check source files before architecture-level decisions
   - **LOW** (filename heuristics, LFS stubs > 50%) — treat as an index only;
     always read the relevant source files before making changes

3. **All tools accept `project_path` as first argument.**
   The correct path for this project is in the "Project Info" section below.
   Use it verbatim — do not guess or construct paths.

"""


# ─────────────────────────────────────────────────────────────
# Quick Reference + 엔진별 세션 플로우
# ─────────────────────────────────────────────────────────────

_QUICK_REF_COMMON = """\
## Quick Reference
Common: `explore_class_semantics` · `trace_gameplay_flow` · `analyze_impact_and_risk` · `inspect_architectural_health` · `get_architecture_advice` · `explain_method_logic` · `suggest_lint_fixes` · `summarize_project_diff`

"""

_SESSION_FLOW_UE5 = """\
## UE5 Tools & Session Flow
UE5: `analyze_ue5_gas` · `analyze_ue5_blueprint_mapping` · `analyze_ue5_behavior_tree` · `analyze_ue5_state_tree` · `analyze_ue5_animation`

```
# New UE5 session
1. get_project_context(path)
2. explore_class_semantics(path, "AMyChar")
3. analyze_ue5_gas(path)
4. analyze_ue5_blueprint_mapping(path, "AMyChar")
5. trace_gameplay_flow(path, "AMyChar", "BeginPlay")
```

"""

_SESSION_FLOW_UNITY = """\
## Unity Tools & Session Flow
Unity: `find_unity_event_bindings` · `analyze_unity_animator`

```
# New Unity session
1. get_project_context(path)
2. explore_class_semantics(path, "GameManager")
3. find_unity_event_bindings(path)
4. analyze_unity_animator(path)
5. analyze_impact_and_risk(path, "ClassName")
```

"""

_SESSION_FLOW_AXMOL = """\
## Axmol Tools & Session Flow
Axmol: `analyze_axmol_events`

```
# New Axmol session
1. get_project_context(path)
2. explore_class_semantics(path, "AppDelegate")
3. analyze_axmol_events(path)
4. analyze_impact_and_risk(path, "ClassName")
```

"""


# ─────────────────────────────────────────────────────────────
# 프로젝트 스냅샷 + MCP 가이드 조립
# ─────────────────────────────────────────────────────────────

def _build_agents_md(project_path: str) -> str:
    profile = detect(project_path)
    src_path = str(profile.source_dirs[0]) if profile.source_dirs else str(profile.root)
    lines: list[str] = []

    # 1. 공통 전문(preamble)
    lines.append(_PREAMBLE.format(
        date=datetime.now().strftime("%Y-%m-%d"),
        engine=profile.display,
        name=profile.name,
    ))

    # 2. 프로젝트 기본 정보
    lines += [
        "## Project Info",
        "",
        "| Field | Value |",
        "|-------|-------|",
        f"| Engine | {profile.display} |",
        f"| Project Name | {profile.name} |",
        f"| Root | `{profile.root}` |",
        f"| Source Path | `{src_path}` |",
        "",
        f"> **Use `{src_path}` as `project_path` in every gdep tool call.**",
        "",
    ]

    # 3. 빠른 스캔 스냅샷
    _append_scan_snapshot(lines, profile, src_path)

    # 4. 엔진 전용 컨텍스트
    if profile.kind == ProjectKind.UNREAL:
        _append_ue5_context(lines, src_path)
    elif profile.kind == ProjectKind.UNITY:
        _append_unity_context(lines, src_path)

    # 5. Quick Reference + 엔진별 세션 플로우
    lines.append(_QUICK_REF_COMMON)
    if profile.kind == ProjectKind.UNREAL:
        lines.append(_SESSION_FLOW_UE5)
    elif profile.kind == ProjectKind.UNITY:
        lines.append(_SESSION_FLOW_UNITY)
    elif profile.kind == ProjectKind.CPP:
        lines.append(_SESSION_FLOW_AXMOL)

    lines += [
        "---",
        "*Regenerate this file: `gdep init <project_path> --force`*",
        "",
    ]

    return "\n".join(lines)


def _append_scan_snapshot(lines: list[str], profile, src_path: str) -> None:
    """스캔 결과 스냅샷 (클래스 수, 결합도 Top5, 순환참조)."""
    try:
        scan_result = runner.scan(profile, circular=True, dead_code=True,
                                  deep=False, include_refs=True, top=10, fmt="json")
        if not (scan_result.ok and scan_result.data):
            return
        d = scan_result.data
        summary = d.get("summary", {})
        coupling = d.get("coupling", [])[:5]
        cycles   = d.get("cycles", [])[:3]
        dead     = d.get("deadNodes", [])[:3]

        lines += [
            "## Codebase Snapshot",
            "",
            f"| Metric | Value |",
            f"|--------|-------|",
            f"| Files | **{summary.get('fileCount', '?')}** |",
            f"| Classes | **{summary.get('classCount', '?')}** |",
            f"| Circular deps | **{summary.get('cycleCount', 0)}** |",
            f"| Dead code candidates | **{summary.get('deadCount', 0)}** |",
            "",
        ]
        if coupling:
            lines += ["### High-coupling classes (likely God Objects — modify with care)", ""]
            for item in coupling:
                lines.append(f"- `{item['name']}` — {item['score']} dependents")
            lines.append("")
        if cycles:
            lines += ["### Known circular dependencies", ""]
            for c in cycles:
                lines.append(f"- {c}")
            lines.append("")
    except Exception:
        pass


def _append_ue5_context(lines: list[str], src_path: str) -> None:
    """UE5 전용: GAS / BT / StateTree 요약 (confidence 정보 포함)."""
    try:
        from .ue5_gas_analyzer import _cached_gas_report
        report = _cached_gas_report(src_path)
        # Asset role counts
        ga_count  = sum(1 for r in report.asset_refs if r.asset_role == "GA")
        ge_count  = sum(1 for r in report.asset_refs if r.asset_role == "GE")
        as_count  = sum(1 for r in report.asset_refs if r.asset_role == "AS")
        abp_count = sum(1 for r in report.asset_refs if r.asset_role == "ABP")
        ref_count = sum(1 for r in report.asset_refs if r.asset_role == "ref")

        count_lines = [
            f"- Abilities (C++): {len(report.abilities)}",
            f"- Effects (C++): {len(report.effects)}",
            f"- AttributeSets: {len(report.attr_sets)}",
            f"- GameplayTags (in assets): {len(report.all_tags)} high-confidence"
            + (f" / {len(report.all_tags_low)} low" if report.all_tags_low else ""),
            f"- GAS .uassets — IS-A: GA {ga_count} / GE {ge_count} / AS {as_count} / ABP {abp_count}"
            f"  |  Referencers: {ref_count}",
        ]
        if count_lines:
            lines += ["## GAS Summary", ""]
            lines += count_lines
            if report.meta:
                lines.append(f"- Analysis confidence: **{report.meta.confidence.value.upper()}**"
                             f" ({report.meta.source_method})")
                lines.append(f"- Coverage: {report.meta.parsed}/{report.meta.scanned} assets"
                             f" ({report.meta.coverage_pct}%)")
                if report.meta.skipped_lfs:
                    lines.append(f"- ⚠ Git LFS stubs skipped: {report.meta.skipped_lfs}"
                                 f" — results may be incomplete")
            lines.append("")
    except Exception:
        pass

    try:
        from .ue5_ai_analyzer import analyze_behavior_tree, analyze_state_tree
        for label, fn in [("BehaviorTrees", analyze_behavior_tree),
                          ("StateTrees",    analyze_state_tree)]:
            result = fn(src_path)
            names = [l.strip("# ").strip()
                     for l in result.splitlines() if l.startswith("## ")]
            if names and "No " not in result[:60]:
                lines += [f"## {label} found", ""]
                for n in names:
                    lines.append(f"- `{n}`")
                lines.append("")
    except Exception:
        pass


def _append_unity_context(lines: list[str], src_path: str) -> None:
    """Unity 전용: Animator Controller 목록."""
    try:
        from .unity_animator import _find_controllers
        controllers = _find_controllers(src_path)
        if controllers:
            lines += ["## Animator Controllers found", ""]
            for c in controllers[:10]:
                lines.append(f"- `{c.stem}`")
            lines.append("")
    except Exception:
        pass



# ─────────────────────────────────────────────────────────────
# Public API
# ─────────────────────────────────────────────────────────────

def build_context_output(project_path: str) -> str:
    """
    get_project_context MCP 도구 및 `gdep context` CLI에서 호출.
    .gdep/AGENTS.md가 있으면 그걸 읽고, 없으면 자동 생성 후 반환.
    """
    profile = detect(project_path)
    agents_md = Path(profile.root) / ".gdep" / "AGENTS.md"

    if agents_md.exists():
        return agents_md.read_text(encoding="utf-8")

    # AGENTS.md 없으면 자동 생성 (저장 + .gitignore 추가 포함)
    try:
        agents_md = write_agents_md(project_path, force=False)
        return agents_md.read_text(encoding="utf-8")
    except Exception:
        return _build_agents_md(project_path)


def _ensure_gitignore(project_root: Path) -> None:
    """
    프로젝트 루트의 .gitignore에 .gdep/ 항목이 없으면 자동 추가.
    Git 저장소가 아닌 경우에도 조용히 스킵한다.
    """
    gitignore = project_root / ".gitignore"
    entry = ".gdep/"
    try:
        if gitignore.exists():
            content = gitignore.read_text(encoding="utf-8")
            # 이미 있으면 추가 안 함 (.gdep/ 또는 .gdep 로 시작하는 라인)
            if any(line.strip() in (entry, ".gdep") or line.strip().startswith(".gdep/")
                   for line in content.splitlines()):
                return
            # 끝에 개행 보장 후 추가
            sep = "" if content.endswith("\n") else "\n"
            gitignore.write_text(content + sep + entry + "\n", encoding="utf-8")
        else:
            # .gitignore 파일 자체가 없으면 생성 (Git 저장소 루트에만)
            if (project_root / ".git").is_dir():
                gitignore.write_text(entry + "\n", encoding="utf-8")
    except Exception:
        pass  # 권한 오류 등은 조용히 무시


def write_agents_md(project_path: str, force: bool = False) -> Path:
    """
    `gdep init` CLI에서 호출.
    .gdep/AGENTS.md를 생성하고 경로를 반환한다.
    force=True 이면 기존 파일을 덮어쓴다.
    """
    profile = detect(project_path)
    gdep_dir = Path(profile.root) / ".gdep"
    gdep_dir.mkdir(exist_ok=True)
    _ensure_gitignore(Path(profile.root))   # .gitignore 자동 추가
    agents_md = gdep_dir / "AGENTS.md"

    if agents_md.exists() and not force:
        return agents_md

    content = _build_agents_md(project_path)
    agents_md.write_text(content, encoding="utf-8")
    return agents_md

