"""Что реально установлено и загружено на этой машине.

Показ загруженного контекста — самая востребованная и нигде не реализованная функция
категории: 18 месяцев жалоб «правила игнорируются» держатся на том, что пользователь
не может увидеть, подхватилось правило или нет. Тихий провал недопустим.
"""

from __future__ import annotations

import os
import platform
import shutil
import sys
from pathlib import Path

from . import __version__
from .phases import phase_dir
from .store import PHASES, STATE_DIR, Run, find_root

OK, WARN, BAD = "✓", "!", "✗"


def _home_claude() -> Path:
    return Path(os.environ.get("CLAUDE_CONFIG_DIR", Path.home() / ".claude"))


def _canon_files() -> list[Path]:
    home = Path.home()
    return [
        _home_claude() / "CLAUDE.md",
        _home_claude() / "AGENTS.md",
        Path.home() / ".claude.json",
        Path.home() / ".claude" / "settings.json",
    ]


def _hook_selftest() -> tuple[bool | None, str]:
    """Хук реально блокирует попытку оркестратора писать в проект."""
    import json
    import subprocess
    import tempfile

    script = Path(__file__).resolve().parent.parent / "hooks" / "chekan_hook.py"
    if not script.is_file():
        return None, f"нет скрипта {script}"

    with tempfile.TemporaryDirectory() as tmp:
        root = Path(tmp)
        (root / STATE_DIR / "probe").mkdir(parents=True)
        (root / STATE_DIR / "active").write_text("probe\n", encoding="utf-8")
        state = {
            "version": 1, "slug": "probe", "title": "самопроверка", "mode": "auto",
            "tier": "T0", "phase": "build", "started_at": "2000-01-01T00:00:00+00:00",
            "updated_at": "2000-01-01T00:00:00+00:00", "finished_at": None,
            "brief_file": "brief.md", "memory_file": None,
            "stages": {p: {"status": "pending", "started_at": None, "finished_at": None}
                       for p in PHASES},
            "gates": {}, "requirements": [], "tasks": [],
        }
        state["stages"]["build"]["status"] = "active"
        (root / STATE_DIR / "probe" / "state.json").write_text(
            json.dumps(state, ensure_ascii=False), encoding="utf-8")

        payload = {
            "hook_event_name": "PreToolUse", "tool_name": "Write", "cwd": str(root),
            "tool_input": {"file_path": str(root / "app.py")},
        }
        try:
            out = subprocess.run(
                [sys.executable, str(script)], input=json.dumps(payload),
                capture_output=True, text=True, timeout=20,
            )
        except (OSError, subprocess.SubprocessError) as exc:
            return None, str(exc)

        if not out.stdout.strip():
            return False, "правка кода оркестратором прошла бы без отказа"
        try:
            decision = json.loads(out.stdout)["hookSpecificOutput"]["permissionDecision"]
        except (ValueError, KeyError) as exc:
            return None, f"непонятный ответ хука ({exc})"
        if decision != "deny":
            return False, f"решение {decision!r} вместо deny"

        payload["agent_id"] = "sub-1"
        sub = subprocess.run(
            [sys.executable, str(script)], input=json.dumps(payload),
            capture_output=True, text=True, timeout=20,
        )
        if sub.stdout.strip():
            return False, "исполнителю-субагенту тоже отказано — это сломало бы сборку"
        return True, "оркестратору отказ, исполнителю проход"


def report(verbose: bool = False) -> int:
    problems = 0
    lines: list[str] = []

    warnings = 0

    def say(mark: str, text: str) -> None:
        nonlocal problems, warnings
        if mark == BAD:
            problems += 1
        elif mark == WARN:
            warnings += 1
        lines.append(f"  {mark} {text}")

    lines.append(f"Чекан {__version__}")
    lines.append("")
    lines.append("Машина:")
    say(OK, f"{platform.system()} {platform.release()} · {platform.machine()}")
    py = sys.version_info
    if (py.major, py.minor) >= (3, 9):
        say(OK, f"Python {py.major}.{py.minor}.{py.micro}")
    else:
        say(BAD, f"Python {py.major}.{py.minor} — нужен 3.9 или новее")
    say(OK if shutil.which("git") else BAD, f"git: {shutil.which('git') or 'не найден'}")

    lines.append("")
    lines.append("Установка:")
    pdir = phase_dir()
    missing = [p for p in PHASES if not (pdir / f"{p}.md").is_file()]
    if missing:
        say(BAD, f"нет правил фаз: {', '.join(missing)} — переустановите пакет")
    else:
        say(OK, f"правила всех {len(PHASES)} фаз на месте ({pdir})")
    amap = pdir.parent / "accept-map.json"
    say(OK if amap.is_file() else BAD, f"карта приёмки: {amap if amap.is_file() else 'нет'}")

    lines.append("")
    lines.append("Канон правил:")
    for path in _canon_files():
        if path.is_file():
            size = path.stat().st_size
            mtime = __import__("datetime").datetime.fromtimestamp(path.stat().st_mtime)
            say(OK, f"{path} — {size} Б, изменён {mtime:%Y-%m-%d}")
        elif path.is_symlink():
            say(BAD, f"{path} — битый симлинк")
        else:
            say(WARN, f"{path} — нет")

    lines.append("")
    lines.append("Скиллы:")
    skills_dir = _home_claude() / "skills"
    if skills_dir.is_dir():
        names = sorted(d.name for d in skills_dir.iterdir() if (d / "SKILL.md").is_file() or d.is_symlink())
        say(OK if names else WARN, f"{skills_dir}: {len(names)} шт.")
        if verbose:
            for name in names:
                lines.append(f"      · {name}")
        broken = [d.name for d in skills_dir.iterdir() if d.is_symlink() and not d.exists()]
        if broken:
            say(BAD, f"битые симлинки: {', '.join(broken)}")
    else:
        say(WARN, f"{skills_dir} — нет")

    lines.append("")
    lines.append("Хуки:")
    plugin_hooks = list((_home_claude() / "plugins" / "cache").glob("*/chekan/*/hooks/hooks.json"))
    if plugin_hooks:
        say(OK, f"конфигурация на месте: {plugin_hooks[0]}")
    else:
        say(WARN, "плагин не установлен: запреты не принуждаются, работают только гейты CLI")

    verdict, detail = _hook_selftest()
    if verdict is True:
        say(OK, f"логика запрета работает: {detail}")
    elif verdict is False:
        say(BAD, f"запрет НЕ сработал: {detail}")
    else:
        say(WARN, f"самопроверку выполнить не удалось: {detail}")
    say(
        WARN,
        "живое срабатывание внутри сессии этим не доказывается: харнесс читает хуки "
        "при СТАРТЕ сессии. Ставили плагин на ходу — включатся в следующей",
    )

    lines.append("")
    lines.append("Харнесс:")
    if os.environ.get("CLAUDECODE") or os.environ.get("CLAUDE_CODE_ENTRYPOINT"):
        say(OK, "Claude Code — есть субагенты, параллель и хуки")
    else:
        say(
            WARN,
            "не Claude Code: параллельные субагенты и живые дозапросы недоступны, "
            "сборка пойдёт последовательно",
        )

    lines.append("")
    lines.append("Прогон:")
    try:
        root = find_root()
        slug = Run.active_slug(root)
        if slug:
            run = Run(root, slug)
            st = run.state
            from .manifest import short_title

            head = short_title(st["title"])
            if st.get("finished_at"):
                say(OK, f"закрыт «{head}» [{slug}] — {st['finished_at'][:16].replace('T', ' ')}")
            else:
                say(OK, f"идёт «{head}» [{slug}], фаза {st['phase']}")
            orphans = [t["id"] for t in st["tasks"] if t["status"] == "done" and not run.green_evidence(t["id"])]
            if orphans:
                say(BAD, f"таски закрыты без доказательств: {', '.join(orphans)}")
        else:
            say(OK, f"активного прогона нет в {root}")
    except Exception as exc:  # noqa: BLE001 — doctor обязан пережить любое состояние
        say(BAD, f"состояние не читается: {exc}")

    lines.append("")
    if problems:
        lines.append(f"Проблем: {problems}. Исправьте отмеченные {BAD}.")
    elif warnings:
        lines.append(
            f"Ошибок нет, но {warnings} предупреждений ({WARN}) — часть принуждения "
            "и синхронизации сейчас не работает. Прочитайте их, это не косметика."
        )
    else:
        lines.append("Проблем нет.")
    print("\n".join(lines))
    return 1 if problems else 0
