"""Tests for `abreka chat` — project context assembly, argv building, prompt rendering."""

from __future__ import annotations

from datetime import datetime, timedelta, timezone
from pathlib import Path

from abreka.cli.chat_cmd import _build_argv, _build_context, _render_system_prompt
from abreka.models import BacklogItem, BacklogStatus, Experiment, ExperimentStatus, WikiPage
from abreka.persistence import (
    create_experiment_dir,
    save_backlog_item,
    save_experiment,
    update_backlog_index_entry,
    update_index_entry,
)
from abreka.wiki import save_wiki_page


_NOW = datetime(2026, 1, 1, tzinfo=timezone.utc)


def _make_experiment(
    abreka_dir: Path,
    exp_id: str,
    *,
    status: ExperimentStatus = ExperimentStatus.COMPLETED,
    hypothesis: str = "Baseline experiment.",
    method: str = "Train the baseline model.",
    metrics: dict | None = None,
    created_at: datetime | None = None,
) -> Experiment:
    exp = Experiment(
        id=exp_id,
        status=status,
        hypothesis=hypothesis,
        method=method,
        metrics=metrics or {},
        created_at=created_at or _NOW,
    )
    create_experiment_dir(abreka_dir, exp_id)
    save_experiment(exp, abreka_dir)
    update_index_entry(abreka_dir, exp)
    return exp


class TestBuildContext:
    def test_collects_project_metadata_history_backlog_and_wiki(self, abreka_project):
        abreka_dir = abreka_project / "abreka"
        (abreka_project / "src" / "test_project" / "models.py").write_text("class Model: ...\n")

        _make_experiment(
            abreka_dir,
            "0001",
            status=ExperimentStatus.COMPLETED,
            hypothesis="Baseline CNN.",
            metrics={"val": {"accuracy": 0.91}},
            created_at=_NOW,
        )
        _make_experiment(
            abreka_dir,
            "0002",
            status=ExperimentStatus.FAILED,
            hypothesis="Try a larger model.",
            created_at=_NOW + timedelta(hours=1),
        )

        open_item = BacklogItem(
            id="0001",
            title="Try stronger augmentation",
            description="The baseline may still be overfitting.",
            priority=3,
            source_exp="0001",
        )
        save_backlog_item(open_item, abreka_dir)
        update_backlog_index_entry(abreka_dir, open_item)

        resolved_item = BacklogItem(
            id="0002",
            title="Already done",
        )
        resolved_item.status = BacklogStatus.RESOLVED
        save_backlog_item(resolved_item, abreka_dir)
        update_backlog_index_entry(abreka_dir, resolved_item)

        save_wiki_page(
            WikiPage(
                slug="dataset-balance",
                title="Dataset class balance",
                summary="9/10 classes uniform.",
                tags=["eda", "dataset"],
            ),
            abreka_dir,
        )
        save_wiki_page(
            WikiPage(
                slug="calibration-rule",
                title="Always check calibration",
                summary="Calibration during eval.",
            ),
            abreka_dir,
        )

        ctx = _build_context(abreka_dir)

        assert ctx["project_name"] == "test-project"
        assert ctx["lib_name"] == "test_project"
        assert ctx["primary_metric"] == "val:accuracy"
        assert ctx["test_command"] == "uv run pytest"
        assert "Achieve high accuracy" in ctx["goal_text"]
        assert "2 total" in ctx["status_summary"]
        assert "completed" in ctx["status_summary"]
        assert "failed" in ctx["status_summary"]
        assert "0001" in ctx["best_experiment"]
        assert "0.91" in ctx["best_experiment"]
        assert "0002" in ctx["recent_experiments"]
        assert "Try stronger augmentation" in ctx["backlog_items"]
        assert "Already done" not in ctx["backlog_items"]
        assert "dataset-balance" in ctx["wiki_pages"]
        assert "calibration-rule" in ctx["wiki_pages"]
        assert "src/test_project/models.py" in ctx["shared_library_listing"]
        assert "abreka/prompts/propose_exploit.md" in ctx["prompt_listing"]

    def test_handles_empty_project_backlog_and_wiki(self, abreka_project):
        abreka_dir = abreka_project / "abreka"

        ctx = _build_context(abreka_dir)

        assert "No experiments yet" in ctx["status_summary"]
        assert "No experiments have recorded" in ctx["best_experiment"]
        assert "No experiments yet" in ctx["recent_experiments"]
        assert "No open backlog items" in ctx["backlog_items"]
        assert "No wiki pages yet" in ctx["wiki_pages"]


class TestRenderSystemPrompt:
    def test_template_substitutes_project_name_and_goal(self, abreka_project):
        abreka_dir = abreka_project / "abreka"

        ctx = _build_context(abreka_dir)
        rendered = _render_system_prompt(ctx)

        assert "test-project" in rendered
        assert "Achieve high accuracy" in rendered
        assert "src/test_project/" in rendered
        assert "abreka/chats/" in rendered


class TestBuildArgv:
    def test_minimal_argv_has_session_dir_and_system_prompt(self, tmp_path):
        chats_dir = tmp_path / "chats"
        argv = _build_argv(
            system_prompt="SYS",
            chats_dir=chats_dir,
            continue_=False,
            resume=False,
            messages=(),
        )
        assert argv[0] == "pi"
        assert "--append-system-prompt" in argv
        assert "SYS" in argv
        assert "--session-dir" in argv
        assert str(chats_dir) in argv
        assert "project" in argv[-1].lower()

    def test_continue_and_resume_pass_through(self, tmp_path):
        chats_dir = tmp_path / "chats"
        argv = _build_argv(
            system_prompt="SYS",
            chats_dir=chats_dir,
            continue_=True,
            resume=False,
            messages=(),
        )
        assert "--continue" in argv

        argv = _build_argv(
            system_prompt="SYS",
            chats_dir=chats_dir,
            continue_=False,
            resume=True,
            messages=(),
        )
        assert "--resume" in argv

    def test_explicit_messages_replace_default(self, tmp_path):
        chats_dir = tmp_path / "chats"
        argv = _build_argv(
            system_prompt="SYS",
            chats_dir=chats_dir,
            continue_=False,
            resume=False,
            messages=("hello", "world"),
        )
        assert argv[-2:] == ["hello", "world"]
