"""Tests for architect config."""

from __future__ import annotations

from pathlib import Path
from unittest.mock import patch

import pytest

from cleave.architect.config import ArchitectConfig
from cleave.architect.errors import ArchitectError


class TestArchitectConfig:
    def _make_config(self, tmp_path: Path, **kwargs) -> ArchitectConfig:
        """Helper to create a config with a valid repo path."""
        (tmp_path / ".git").mkdir()
        defaults = {
            "directive": "Test directive",
            "repo_path": tmp_path,
        }
        defaults.update(kwargs)
        return ArchitectConfig(**defaults)

    def test_defaults(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path)
        assert cfg.planner_model == "opus"
        assert cfg.executor_model == "opus"
        assert cfg.executor_planner_model == "sonnet"
        assert cfg.max_budget_usd == 200.0
        assert cfg.phase_budget_usd == 50.0
        assert cfg.child_budget_usd == 15.0
        assert cfg.timeout_seconds == 86400
        assert cfg.max_phases == 10
        assert cfg.max_depth == 3
        assert cfg.circuit_breaker_threshold == 2
        assert cfg.confirm is False
        assert cfg.db_path is None

    def test_validate_success(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path)
        cfg.validate()  # Should not raise

    def test_validate_empty_directive(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, directive="   ")
        with pytest.raises(ArchitectError, match="empty"):
            cfg.validate()

    def test_validate_no_git_repo(self, tmp_path: Path) -> None:
        cfg = ArchitectConfig(directive="test", repo_path=tmp_path)
        with pytest.raises(ArchitectError, match="not a git"):
            cfg.validate()

    def test_validate_bad_budget(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, max_budget_usd=-1)
        with pytest.raises(ArchitectError, match="positive"):
            cfg.validate()

    def test_validate_phase_exceeds_total(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, max_budget_usd=10, phase_budget_usd=20)
        with pytest.raises(ArchitectError, match="cannot exceed"):
            cfg.validate()

    def test_validate_bad_max_phases(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, max_phases=0)
        with pytest.raises(ArchitectError, match="max_phases"):
            cfg.validate()

    def test_validate_bad_max_phases_high(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, max_phases=25)
        with pytest.raises(ArchitectError, match="max_phases"):
            cfg.validate()

    def test_validate_bad_depth(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, max_depth=0)
        with pytest.raises(ArchitectError, match="max_depth"):
            cfg.validate()

    def test_validate_bad_timeout(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, timeout_seconds=0)
        with pytest.raises(ArchitectError, match="timeout_seconds"):
            cfg.validate()

    def test_str_repo_path_coercion(self, tmp_path: Path) -> None:
        (tmp_path / ".git").mkdir()
        cfg = ArchitectConfig(directive="test", repo_path=str(tmp_path))
        assert isinstance(cfg.repo_path, Path)

    def test_to_dict(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path)
        d = cfg.to_dict()
        assert d["directive"] == "Test directive"
        assert d["repo_path"] == str(tmp_path)
        assert d["max_budget_usd"] == 200.0
        assert d["db_path"] is None

    def test_from_dict_roundtrip(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path, max_budget_usd=100.0)
        d = cfg.to_dict()
        restored = ArchitectConfig.from_dict(d)
        assert restored.directive == cfg.directive
        assert restored.repo_path == cfg.repo_path
        assert restored.max_budget_usd == 100.0

    def test_from_dict_ignores_unknown_fields(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path)
        d = cfg.to_dict()
        d["unknown_field"] = "ignored"
        restored = ArchitectConfig.from_dict(d)
        assert restored.directive == cfg.directive

    def test_find_claude_cli_not_found(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path)
        with patch("shutil.which", return_value=None):
            with pytest.raises(ArchitectError, match="claude"):
                cfg.find_claude_cli()

    def test_find_claude_cli_found(self, tmp_path: Path) -> None:
        cfg = self._make_config(tmp_path)
        with patch("shutil.which", return_value="/usr/bin/claude"):
            assert cfg.find_claude_cli() == "/usr/bin/claude"
