"""--skip-plan: authored doc runs straight to build, zero planner calls."""

from __future__ import annotations

import subprocess
import sys
from pathlib import Path

import pytest
import yaml

import adw_plan_build_test_quality as adw
from adw_modules import agents
from adw_modules.data_types import BuildOutput, PlanOutput


def _git(repo: Path, *args: str) -> str:
    result = subprocess.run(["git", "-C", str(repo), *args],
                            check=True, capture_output=True, text=True)
    return result.stdout.strip()


def _repo(tmp_path: Path) -> Path:
    repo = tmp_path / "repo"
    repo.mkdir()
    _git(repo, "init", "-b", "main")
    _git(repo, "config", "user.email", "factory@test")
    _git(repo, "config", "user.name", "Factory Test")
    (repo / "base.txt").write_text("base\n")
    _git(repo, "add", "base.txt")
    _git(repo, "commit", "-m", "base")
    return repo


def _overlay(tmp_path: Path, db: Path, data: Path) -> Path:
    overlay = tmp_path / "config.yaml"
    overlay.write_text(yaml.safe_dump({
        "defaults": {"data_dir": str(data), "git_branch_mode": "create"},
        "observability": {"db": str(db)},
        "workflow": {"max_repair_iterations": 1, "max_revision_iterations": 1},
        "quality": {
            "test": {"argv": ["true"]},
            "lint": {"argv": ["true"], "operation": "lint"},
            "typecheck": {"argv": ["true"], "operation": "typecheck"},
            "build": {"argv": ["true"]},
        },
    }))
    return overlay


def _plan_doc(repo: Path, *, empty: bool = False) -> Path:
    doc = repo / "docs" / "plans" / "2026-08-15-example.md"
    doc.parent.mkdir(parents=True)
    doc.write_text("" if empty else "# Example plan\n\noutcome: ship it.\n")
    if not empty:                    # authored docs are already tracked, like the real thing
        _git(repo, "add", str(doc.relative_to(repo)))
        _git(repo, "commit", "-m", "add plan doc")
    return doc


def test_skip_plan_runs_build_with_zero_planner_calls(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    repo = _repo(tmp_path)
    doc = _plan_doc(repo)
    db = tmp_path / "trace.db"
    overlay = _overlay(tmp_path, db, tmp_path / "data")

    calls: list[str] = []

    def fake_execute(run, phase, call, _baseline_commit):
        calls.append(phase.params.owner)
        assert isinstance(call.previous, PlanOutput)
        assert call.previous.artifacts
        assert Path(call.previous.artifacts[0]).read_text() == doc.read_text()
        path = repo / "implementation.txt"
        path.write_text("built\n")
        run.claim_paths(["implementation.txt"])
        return BuildOutput(status="success", summary="build",
                           changed_files=[str(path)], commit_message="build")

    monkeypatch.setattr(agents, "execute", fake_execute)
    monkeypatch.setattr(agents, "validate", lambda _cfg, _required: None)
    monkeypatch.chdir(repo)

    rc = adw.main(str(doc), str(overlay), "skip1", skip_plan=True)

    assert rc == 0
    assert calls == ["builder"]           # planner never invoked


def test_skip_plan_records_honest_plan_phase_row(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    import sqlite3

    repo = _repo(tmp_path)
    doc = _plan_doc(repo)
    db = tmp_path / "trace.db"
    overlay = _overlay(tmp_path, db, tmp_path / "data")

    def fake_execute(run, phase, call, _baseline_commit):
        path = repo / "implementation.txt"
        path.write_text("built\n")
        run.claim_paths(["implementation.txt"])
        return BuildOutput(status="success", summary="build",
                           changed_files=[str(path)], commit_message="build")

    monkeypatch.setattr(agents, "execute", fake_execute)
    monkeypatch.setattr(agents, "validate", lambda _cfg, _required: None)
    monkeypatch.chdir(repo)

    assert adw.main(str(doc), str(overlay), "skip2", skip_plan=True) == 0

    conn = sqlite3.connect(db)
    try:
        row = conn.execute(
            "SELECT kind, owner, description, status FROM phases "
            "WHERE adw_id='skip2' AND name='plan'"
        ).fetchone()
    finally:
        conn.close()

    assert row is not None
    kind, owner, description, status = row
    assert kind == "code"
    assert owner == "planner"
    assert status == "success"
    assert "skip" in description.lower()


def test_skip_plan_refuses_freeform_prompt(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    repo = _repo(tmp_path)
    db = tmp_path / "trace.db"
    overlay = _overlay(tmp_path, db, tmp_path / "data")
    calls: list[str] = []
    monkeypatch.setattr(agents, "execute", lambda *a, **k: calls.append("called"))
    monkeypatch.chdir(repo)

    rc = adw.main("just brainstorm this with me", str(overlay), "skip3", skip_plan=True)

    assert rc != 0
    assert calls == []
    assert not db.exists()               # refused before any session/tracer side effect


def test_skip_plan_refuses_missing_or_empty_path(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    repo = _repo(tmp_path)
    db = tmp_path / "trace.db"
    overlay = _overlay(tmp_path, db, tmp_path / "data")
    empty_doc = _plan_doc(repo, empty=True)
    monkeypatch.chdir(repo)

    missing_rc = adw.main(str(repo / "docs" / "plans" / "nope.md"), str(overlay), "skip4", skip_plan=True)
    empty_rc = adw.main(str(empty_doc), str(overlay), "skip5", skip_plan=True)

    assert missing_rc != 0
    assert empty_rc != 0
    assert not db.exists()


def test_skip_plan_refuses_path_outside_plan_dirs(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
    repo = _repo(tmp_path)
    db = tmp_path / "trace.db"
    overlay = _overlay(tmp_path, db, tmp_path / "data")
    stray = repo / "README.md"
    stray.write_text("# not a plan doc\n")
    monkeypatch.chdir(repo)

    rc = adw.main(str(stray), str(overlay), "skip6", skip_plan=True)

    assert rc != 0
    assert not db.exists()
