"""Что делать дальше.

`chekan next` — единственная точка, которая говорит агенту, где он находится и что от него
требуется. Правила фазы печатаются здесь и только здесь: они не попадают в контекст
заранее и не читаются повторно «на всякий случай». Забыл правила — вызови ещё раз,
это стоит копейки против тридцати килобайт, лежащих в контексте весь прогон.
"""

from __future__ import annotations

from pathlib import Path

from . import gates
from .store import PHASES, Run

PHASE_RU = {
    "preflight": "Подготовка",
    "manifest": "Требования",
    "briefing": "Брифинг",
    "spec": "Спецификация",
    "plan": "План",
    "build": "Разработка",
    "accept": "Приёмка",
}

# Ярус решает, какие фазы вообще нужны. Церемония масштабируется под задачу,
# а не наоборот: 1300 строк markdown ради вывода даты — дефект маршрутизации.
TIER_PHASES = {
    "T0": ["preflight", "build", "accept"],
    "T1": ["preflight", "manifest", "briefing", "plan", "build", "accept"],
    "T2": PHASES,
    "T3": PHASES,
}

# Масштаб работы человеческими словами. Коды T0–T3 остаются в состоянии как
# машинные значения, но пользователю «ярус T2» не говорит ничего.
SCALE_RU = {
    "T0": "точечная",
    "T1": "малая",
    "T2": "средняя",
    "T3": "крупная",
}

SCALE_CODE = {v: k for k, v in SCALE_RU.items()}

TIER_HINT = {
    "T0": "правка в одну-две точки, задачи не нарезаем",
    "T1": "2–3 задачи",
    "T2": "4–8 задач",
    "T3": "9–16 задач, больше — дробить сам прогон",
}


def scale_label(tier: str) -> str:
    return f"{SCALE_RU.get(tier, tier)} ({TIER_HINT.get(tier, '')})"

GATE_AFTER = {"briefing": "G1", "spec": "G2", "plan": "G3", "accept": "G4"}


def phase_dir() -> Path:
    return Path(__file__).resolve().parent.parent / "phases"


def phase_text(name: str) -> str:
    path = phase_dir() / f"{name}.md"
    if not path.is_file():
        return f"(нет файла правил {path.name} — это дефект установки, сообщите `chekan doctor`)"
    return path.read_text(encoding="utf-8").strip()


def active_phases(run: Run) -> list[str]:
    return TIER_PHASES.get(run.state["tier"], PHASES)


def applicable_gates(run: Run) -> list[str]:
    """Гейты фаз, которых в этом ярусе нет, не проверяются.

    На T1 фазы спецификации не существует — значит и гейт покрытия спеки к прогону
    неприменим. Требовать его означало бы держать вечно красный пункт, а красное,
    которое ничего не значит, быстро перестают читать вообще.
    """
    live = set(active_phases(run))
    return [g for phase, g in GATE_AFTER.items() if phase in live]


def resolve(run: Run) -> tuple[str, list[str]]:
    """Возвращает фазу, в которой мы находимся, и что мешает её покинуть."""
    st = run.state
    plan = active_phases(run)
    current = st["phase"]
    if current not in plan:
        current = next((p for p in plan if st["stages"][p]["status"] != "done"), plan[-1])

    gate = GATE_AFTER.get(current)
    blockers = gates.check(run, gate) if gate else []
    if current == "build":
        blockers = _build_blockers(run)
    if st["stages"].get(current, {}).get("status") == "skipped":
        blockers = []
    return current, blockers


def _build_blockers(run: Run) -> list[str]:
    problems = gates.build_invariant(run)
    open_tasks = [t for t in run.state["tasks"] if t["status"] != "done"]
    if open_tasks and not problems:
        problems = [
            f"таск {t['id']} — {t['title']} ({t['status']})" for t in open_tasks
        ]
    return problems


def advance(run: Run) -> str | None:
    """Двигает прогон на следующую фазу, если текущая закрыта. Возвращает новую фазу."""
    current, blockers = resolve(run)
    if blockers:
        return None
    plan = active_phases(run)
    idx = plan.index(current)
    if idx + 1 >= len(plan):
        st = run.state
        st["stages"][current]["status"] = "done"
        run.save(st)
        return None
    nxt = plan[idx + 1]
    if nxt == "briefing" and run.state["mode"] == "auto":
        # Пропуск обязан быть видимым: «вопросов не задавали» — это решение режима,
        # а не забытый шаг, и на панели разница принципиальна. Указатель фазы при
        # этом обязан сдвинуться, иначе следующий шаг снова упрётся в тот же пропуск.
        run.enter_phase(nxt)  # закрывает предыдущую фазу как положено
        run.skip_phase(nxt, "режим auto — вопросы не задаются")
        return advance(run)
    run.enter_phase(nxt)
    return nxt


def render(run: Run) -> str:
    st = run.state
    current, blockers = resolve(run)
    plan = active_phases(run)
    skipped = [p for p in PHASES if p not in plan]

    lines = [
        f"Прогон: {st['title']}",
        f"Слаг: {st['slug']}   Режим: {st['mode']}   Масштаб: {scale_label(st['tier'])}",
        f"Фаза {plan.index(current) + 1} из {len(plan)}: {PHASE_RU[current]}",
    ]
    if skipped:
        lines.append(
            "Пропущены по масштабу: " + ", ".join(PHASE_RU[p] for p in skipped)
        )

    reqs = st["requirements"]
    if reqs:
        done = sum(1 for r in reqs if r["status"] == "done")
        lines.append(f"Требования: {done} из {len(reqs)} закрыто")
    tasks = st["tasks"]
    if tasks:
        done = sum(1 for t in tasks if t["status"] == "done")
        lines.append(f"Таски: {done} из {len(tasks)} закрыто")

    lines += ["", "─" * 60, "", phase_text(current), "", "─" * 60, ""]

    if st.get("finished_at"):
        lines += [
            f"Прогон ЗАКРЫТ {st['finished_at'][:16].replace('T', ' ')}.",
            "Отчёт: report.md рядом с состоянием. Новая работа — новый прогон: `chekan init`.",
        ]
        return "\n".join(lines)

    gate = GATE_AFTER.get(current)
    if blockers:
        head = (
            f"Гейт {gate} ({gates.GATE_TITLE[gate]}) не пройден:"
            if gate
            else "Фаза не закрыта:"
        )
        lines.append(head)
        lines += [f"  · {b}" for b in blockers]
        lines.append("")
        lines.append("Пока это не закрыто, следующая фаза не начинается.")
    else:
        if gate:
            lines.append(f"Гейт {gate} ({gates.GATE_TITLE[gate]}) пройден.")
        plan_idx = plan.index(current)
        if plan_idx + 1 < len(plan):
            lines.append(f"Дальше: `chekan advance` → {PHASE_RU[plan[plan_idx + 1]]}")
        else:
            lines.append("Это последняя фаза. Закрывайте прогон: `chekan report`")

    if current == "build" and tasks:
        ready = run.ready_tasks()
        if ready:
            lines += ["", "Готовы к запуску (зависимости закрыты):"]
            lines += [f"  · {t['id']} — {t['title']}  [зона {t['zone'] or '—'}]" for t in ready[:3]]
            if len(ready) > 3:
                lines.append(f"  (ещё {len(ready) - 3}; потолок — 3 в полёте)")

    return "\n".join(lines)
