"""Передача прогона другому агенту.

Смысл всей конструкции в том, что состояние лежит на диске, а не в чьём-то контексте.
Значит любой агент — Claude Code, Codex, Antigravity, Gemini, Grok, opencode — может
подхватить прогон с нуля. Этот модуль печатает всё, что для этого нужно, одним
выводом: кто ты, что нельзя, где мы, что дальше.

Формат намеренно не markdown-специфичный и не привязан к харнессу: его читает модель,
а не рендерер.
"""

from __future__ import annotations

import os
import shutil
from pathlib import Path

from . import gates, phases
from .store import Run

RULES = """ТРИ ЗАПРЕТА — они принуждаются кодом, а не доверием.

1. Не правь файлы в `.chekan/` руками. Ни состояние, ни журнал. Только через `chekan`.
   Битая запятая в состоянии стоит дороже любой сэкономленной команды.

2. Не отмечай работу выполненной. Отметку ставит код возврата:
   `chekan run "<команда>" --task NN`, затем `chekan task done NN`.
   Без зелёного прогона CLI откажет — это не придирка, а весь смысл инструмента.

3. Не пиши код проекта сам. Ты оркестратор: твоя клавиатура достаёт до `.chekan/`,
   файла памяти и git. Код пишут субагенты со свежим контекстом. Если субагентов в
   твоём харнессе нет — пиши сам, но каждый таск всё равно закрывай доказательством."""


def _agent_hint() -> str:
    if os.environ.get("CLAUDECODE") or os.environ.get("CLAUDE_CODE_ENTRYPOINT"):
        return "Claude Code: есть субагенты, параллель и хуки-запреты."
    for var, name in (
        ("CODEX_HOME", "Codex"),
        ("CURSOR_TRACE_ID", "Cursor"),
        ("GEMINI_CLI", "Gemini"),
        ("ANTIGRAVITY_HOME", "Antigravity"),
    ):
        if os.environ.get(var):
            return f"{name}: параллельные субагенты и живые дозапросы могут быть недоступны — веди сборку последовательно."
    return (
        "Харнесс не опознан. Если параллельных субагентов нет — веди сборку "
        "последовательно, по одному таску, это допустимо и ничего не ломает."
    )


def _git_state(run: Run) -> list[str]:
    from . import checkpoint
    from .store import git

    branch = git(run.root, "rev-parse", "--abbrev-ref", "HEAD") or "?"
    last = git(run.root, "log", "-1", "--format=%h %s") or "коммитов нет"
    dirty = git(run.root, "status", "--porcelain")
    n = len([ln for ln in dirty.splitlines() if ln.strip()])
    state = "чистая" if not n else f"{n} изменений — предыдущий агент мог оборваться на середине"
    lines = [f"Git: ветка {branch} · последний {last}", f"     рабочая копия {state}"]

    # Грязное дерево — единственное место, где контрольная точка кому-то нужна.
    # Поэтому команда восстановления печатается дословно: её копируют и
    # выполняют, а не пересказывают.
    rows = checkpoint.dirty_paths(run.root)
    if not rows:
        return lines
    lines.append("     не сохранено:")
    lines += [f"       {row}" for row in rows]
    point = checkpoint.latest(run.root, run.slug)
    if point is None:
        lines.append("     контрольных точек этого прогона нет: их снимает `chekan run`.")
    else:
        ref, sha = point
        lines += [
            f"     последняя контрольная точка: {ref} → {sha[:8]}",
            f"     восстановить её: {checkpoint.restore_command(ref)}",
        ]
    return lines


def build(run: Run, full: bool = False) -> str:
    st = run.state
    current, blockers = phases.resolve(run)
    plan = phases.active_phases(run)
    where = shutil.which("chekan") or "python3 -m chekan"

    reqs = st["requirements"]
    tasks = st["tasks"]
    done_r = sum(1 for r in reqs if r["status"] == "done")
    done_t = sum(1 for t in tasks if t["status"] == "done")

    out: list[str] = []
    out += [
        "=" * 70,
        "ПЕРЕДАЧА ПРОГОНА ЧЕКАНА",
        "=" * 70,
        "",
        "Ты продолжаешь чужую работу. Весь контекст на диске — диалога предыдущего",
        "агента у тебя нет и не нужно: файлы и есть память.",
        "",
        f"Задача:  {st['title']}",
        f"Каталог: {run.dir}",
        f"Команда: {where}",
        f"Режим {st['mode']} · масштаб {phases.SCALE_RU.get(st['tier'], st['tier'])} · {_agent_hint()}",
    ]
    out += _git_state(run)
    out += [
        "",
        RULES,
        "",
        "-" * 70,
        "ГДЕ МЫ",
        "-" * 70,
        "",
        f"Фаза {plan.index(current) + 1} из {len(plan)}: {phases.PHASE_RU[current]}",
    ]

    skipped = [
        (phases.PHASE_RU[p], st["stages"][p].get("reason", ""))
        for p in plan
        if st["stages"][p]["status"] == "skipped"
    ]
    if skipped:
        out.append("Пропущено: " + ", ".join(f"{n} ({r})" if r else n for n, r in skipped))
    if reqs:
        out.append(f"Требования: {done_r} из {len(reqs)} закрыто")
    if tasks:
        out.append(f"Таски: {done_t} из {len(tasks)} закрыто")

    for name in phases.applicable_gates(run):
        entry = st.get("gates", {}).get(name, {})
        mark = {"passed": "пройден", "failed": "НЕ пройден"}.get(entry.get("status"), "не проверялся")
        out.append(f"  {name} {gates.GATE_TITLE[name]}: {mark}")

    unproven = [t["id"] for t in tasks if t["status"] == "done" and not run.green_evidence(t["id"])]
    if unproven:
        out += ["", f"ВНИМАНИЕ: таски закрыты без доказательств: {', '.join(unproven)}"]

    out += ["", "-" * 70, "ЧТО ДАЛЬШЕ", "-" * 70, ""]
    if st.get("finished_at"):
        out += [
            f"Прогон ЗАКРЫТ {st['finished_at'][:16].replace('T', ' ')}. Отчёт: "
            f"{run.dir.name}/report.md",
            "Новая работа — новый прогон: `chekan init \"<что нужно построить>\"`.",
        ]
    elif blockers:
        out.append("Прежде чем идти дальше, закрой это:")
        out += [f"  · {b}" for b in blockers]
    else:
        out.append("Текущая фаза закрыта. Следующий шаг: `chekan advance`")

    open_tasks = [t for t in tasks if t["status"] == "in-progress"]
    if open_tasks:
        out += [
            "",
            "Таск висит в работе без закрытия. Полуприменённый таск хуже свежего —",
            "прогони его заново с нуля, а не догадывайся, что успел предыдущий агент.",
        ]
        for t in open_tasks:
            rows = run.evidence(t["id"])
            out += [
                "",
                f"  {t['id']} — {t['title']}",
                f"    зона:       {t.get('zone') or '—'}",
                f"    требования: {', '.join(t.get('requirements') or []) or '—'}",
                f"    проверка:   {t.get('verify') or '— не назначена, задайте `chekan task set … --verify`'}",
                f"    прогонов:   {len(rows)}"
                + (f", последний код {rows[-1]['exit']}" if rows else ""),
            ]

    unfinished = [r for r in reqs if r["status"] not in ("done", "dropped", "deferred")]
    if unfinished:
        out += ["", "Незакрытые требования:"]
        out += [f"  · {r['id']} [{r['status']}] {r['quote'][:70]}" for r in unfinished[:6]]

    ready = run.ready_tasks()
    if current == "build" and ready:
        out += ["", "Готовы к запуску (зависимости закрыты):"]
        out += [f"  · {t['id']} — {t['title']}  [зона {t['zone'] or '—'}]" for t in ready[:3]]

    if full:
        out += [
            "",
            "-" * 70,
            "ПРАВИЛА ТЕКУЩЕЙ ФАЗЫ",
            "-" * 70,
            "",
            phases.phase_text(current),
        ]
    out += [
        "",
        "=" * 70,
        "Правила текущей фазы целиком: `chekan next`. Подробности таска: `chekan task show NN`.",
        "Команду проверки можно переназначить: `chekan task set NN --verify \"...\"`.",
        "=" * 70,
    ]
    return "\n".join(out)


def resume_hint(root: Path) -> str | None:
    """Одна строка для файла инструкций агента, если в проекте есть незакрытый прогон."""
    slug = Run.active_slug(root)
    if not slug:
        return None
    try:
        state = Run(root, slug).state
    except Exception:
        return None
    if state.get("finished_at"):
        return None
    return (
        f"В этом проекте идёт прогон Чекана «{state['title'][:60]}» "
        f"(фаза {state['phase']}). Продолжить: `chekan handoff`."
    )
