"""Unit tests for `aastf init` configuration wizard."""

from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

import pytest
from typer.testing import CliRunner

from aastf.cli.app import app
from aastf.cli.commands.init import (
    _build_yaml,
    _default_config,
)

runner = CliRunner()

# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def _parse_yaml_flat(text: str) -> dict[str, str | list[str]]:
    """Minimal YAML-ish parser for our flat config (no pyyaml dependency).

    Returns top-level scalar keys as strings and list keys as list[str].
    Ignores comments and blank lines.
    """
    result: dict[str, str | list[str]] = {}
    current_key: str | None = None
    for line in text.splitlines():
        stripped = line.strip()
        if not stripped or stripped.startswith("#"):
            current_key = None if not stripped.startswith("#") else current_key
            continue
        if stripped.startswith("- ") and current_key is not None:
            val = stripped[2:].strip().strip('"')
            existing = result.get(current_key)
            if isinstance(existing, list):
                existing.append(val)
            else:
                result[current_key] = [val]
            continue
        if ":" in stripped:
            key, _, value = stripped.partition(":")
            key = key.strip()
            value = value.strip().strip('"')
            current_key = key
            if value == "[]":
                result[key] = []
            elif value:
                result[key] = value
            # else: value is empty, next lines may be list items
    return result


# ---------------------------------------------------------------------------
# _default_config
# ---------------------------------------------------------------------------


class TestDefaultConfig:
    def test_has_all_required_keys(self) -> None:
        config = _default_config()
        for key in ("agent_module", "adapter", "categories", "fail_on",
                     "output_dir", "formats", "timeout"):
            assert key in config, f"Missing key: {key}"

    def test_adapter_is_valid(self) -> None:
        config = _default_config()
        assert config["adapter"] in ("langgraph", "crewai", "openai_agents", "pydantic_ai")

    def test_categories_default_empty(self) -> None:
        config = _default_config()
        assert config["categories"] == []


# ---------------------------------------------------------------------------
# _build_yaml
# ---------------------------------------------------------------------------


class TestBuildYaml:
    def test_roundtrip_defaults(self) -> None:
        config = _default_config()
        yaml_text = _build_yaml(config)
        parsed = _parse_yaml_flat(yaml_text)
        assert parsed["agent_module"] == config["agent_module"]
        assert parsed["adapter"] == config["adapter"]
        assert parsed["fail_on"] == config["fail_on"]
        assert parsed["output_dir"] == config["output_dir"]
        assert parsed["timeout"] == str(config["timeout"])

    def test_formats_list(self) -> None:
        config = _default_config()
        yaml_text = _build_yaml(config)
        parsed = _parse_yaml_flat(yaml_text)
        assert isinstance(parsed["formats"], list)
        assert "console" in parsed["formats"]
        assert "json" in parsed["formats"]

    def test_categories_empty(self) -> None:
        config = _default_config()
        yaml_text = _build_yaml(config)
        parsed = _parse_yaml_flat(yaml_text)
        assert parsed["categories"] == []

    def test_categories_nonempty(self) -> None:
        config = _default_config()
        config["categories"] = ["ASI01", "ASI05"]
        yaml_text = _build_yaml(config)
        parsed = _parse_yaml_flat(yaml_text)
        assert parsed["categories"] == ["ASI01", "ASI05"]

    def test_yaml_has_header_comment(self) -> None:
        yaml_text = _build_yaml(_default_config())
        assert "# AASTF Configuration" in yaml_text
        assert "# Generated by aastf init" in yaml_text


# ---------------------------------------------------------------------------
# CLI integration (non-interactive)
# ---------------------------------------------------------------------------


class TestInitNonInteractive:
    def test_creates_file(self, tmp_path: Path) -> None:
        out = tmp_path / "aastf.yaml"
        result = runner.invoke(app, ["init", "--yes", "--output", str(out)])
        assert result.exit_code == 0, result.output
        assert out.exists()

    def test_file_content_valid(self, tmp_path: Path) -> None:
        out = tmp_path / "aastf.yaml"
        runner.invoke(app, ["init", "--yes", "--output", str(out)])
        text = out.read_text(encoding="utf-8")
        parsed = _parse_yaml_flat(text)
        assert parsed["adapter"] == "langgraph"
        assert parsed["fail_on"] == "HIGH"
        assert "console" in parsed["formats"]

    def test_default_output_path(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.chdir(tmp_path)
        result = runner.invoke(app, ["init", "--yes"])
        assert result.exit_code == 0
        assert (tmp_path / "aastf.yaml").exists()


# ---------------------------------------------------------------------------
# Overwrite prompt
# ---------------------------------------------------------------------------


class TestOverwritePrompt:
    def test_aborts_when_user_declines(self, tmp_path: Path) -> None:
        out = tmp_path / "aastf.yaml"
        out.write_text("existing", encoding="utf-8")
        with patch("aastf.cli.commands.init.Confirm.ask", return_value=False):
            result = runner.invoke(app, ["init", "--output", str(out)])
        assert result.exit_code == 0
        assert out.read_text(encoding="utf-8") == "existing"

    def test_overwrites_when_user_accepts(self, tmp_path: Path) -> None:
        out = tmp_path / "aastf.yaml"
        out.write_text("existing", encoding="utf-8")
        with patch("aastf.cli.commands.init.Confirm.ask", return_value=True), \
             patch("aastf.cli.commands.init._interactive_config", return_value=_default_config()):
            result = runner.invoke(app, ["init", "--output", str(out)])
        assert result.exit_code == 0
        assert "AASTF Configuration" in out.read_text(encoding="utf-8")

    def test_non_interactive_skips_prompt(self, tmp_path: Path) -> None:
        out = tmp_path / "aastf.yaml"
        out.write_text("existing", encoding="utf-8")
        result = runner.invoke(app, ["init", "--yes", "--output", str(out)])
        assert result.exit_code == 0
        assert "AASTF Configuration" in out.read_text(encoding="utf-8")
