"""Tests for promptkit.config module."""

import json
from pathlib import Path
from typing import Optional

import pytest

from promptkit.config import Config, DEFAULT_CONFIG


class TestConfigInitProject:
    """Tests for Config.init_project class method."""

    def test_init_creates_promptkit_dir(self, tmp_path: Path) -> None:
        Config.init_project(tmp_path)
        assert (tmp_path / ".promptkit").is_dir()

    def test_init_creates_prompts_subdir(self, tmp_path: Path) -> None:
        Config.init_project(tmp_path)
        assert (tmp_path / ".promptkit" / "prompts").is_dir()

    def test_init_creates_tests_subdir(self, tmp_path: Path) -> None:
        Config.init_project(tmp_path)
        assert (tmp_path / ".promptkit" / "tests").is_dir()

    def test_init_creates_cache_subdir(self, tmp_path: Path) -> None:
        Config.init_project(tmp_path)
        assert (tmp_path / ".promptkit" / "cache").is_dir()

    def test_init_creates_config_json(self, tmp_path: Path) -> None:
        Config.init_project(tmp_path)
        config_file = tmp_path / ".promptkit" / "config.json"
        assert config_file.exists()
        data = json.loads(config_file.read_text())
        assert data["version"] == "1"

    def test_init_creates_gitignore(self, tmp_path: Path) -> None:
        Config.init_project(tmp_path)
        gitignore = tmp_path / ".promptkit" / ".gitignore"
        assert gitignore.exists()
        text = gitignore.read_text()
        assert "cache/" in text

    def test_init_returns_config_instance(self, tmp_path: Path) -> None:
        cfg = Config.init_project(tmp_path)
        assert isinstance(cfg, Config)
        assert cfg.is_initialized()

    def test_init_idempotent(self, tmp_path: Path) -> None:
        Config.init_project(tmp_path)
        Config.init_project(tmp_path)
        assert (tmp_path / ".promptkit" / "config.json").exists()


class TestConfigFindProject:
    """Tests for Config._find_project_dir and constructor discovery."""

    def test_finds_project_in_cwd(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        Config.init_project(tmp_path)
        monkeypatch.chdir(tmp_path)
        cfg = Config()
        assert cfg.is_initialized()

    def test_finds_project_in_parent(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        Config.init_project(tmp_path)
        child = tmp_path / "subdir" / "deep"
        child.mkdir(parents=True)
        monkeypatch.chdir(child)
        cfg = Config()
        assert cfg.is_initialized()

    def test_no_project_found(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.chdir(tmp_path)
        cfg = Config()
        assert not cfg.is_initialized()


class TestConfigGetSet:
    """Tests for Config.get / Config.set."""

    def test_get_top_level_key(self, config: Config) -> None:
        assert config.get("version") == "1"

    def test_get_nested_key(self, config: Config) -> None:
        pricing = config.get("pricing.gpt-4o")
        assert pricing is not None
        assert "input" in pricing

    def test_get_missing_key_returns_default(self, config: Config) -> None:
        assert config.get("nonexistent", "fallback") == "fallback"

    def test_set_and_get(self, config: Config) -> None:
        config.set("custom_key", 42)
        assert config.get("custom_key") == 42

    def test_set_nested_key(self, config: Config) -> None:
        config.set("custom.nested.key", "deep_value")
        assert config.get("custom.nested.key") == "deep_value"


class TestConfigSave:
    """Tests for Config.save."""

    def test_save_persists_changes(self, config: Config, initialized_project: Path) -> None:
        config.set("custom", "persisted")
        config.save()
        loaded = Config(project_dir=initialized_project)
        assert loaded.get("custom") == "persisted"

    def test_save_without_project_raises(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.chdir(tmp_path)
        cfg = Config()
        with pytest.raises(RuntimeError):
            cfg.save()


class TestConfigProperties:
    """Tests for Config properties and helpers."""

    def test_prompts_dir(self, config: Config) -> None:
        assert config.prompts_dir.name == "prompts"
        assert config.prompts_dir.parent.name == ".promptkit"

    def test_tests_dir(self, config: Config) -> None:
        assert config.tests_dir.name == "tests"
        assert config.tests_dir.parent.name == ".promptkit"

    def test_prompts_dir_raises_without_project(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.chdir(tmp_path)
        cfg = Config()
        with pytest.raises(RuntimeError):
            _ = cfg.prompts_dir

    def test_tests_dir_raises_without_project(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.chdir(tmp_path)
        cfg = Config()
        with pytest.raises(RuntimeError):
            _ = cfg.tests_dir

    def test_get_pricing_existing_model(self, config: Config) -> None:
        pricing = config.get_pricing("gpt-4o")
        assert pricing is not None
        assert "input" in pricing and "output" in pricing

    def test_get_pricing_unknown_model(self, config: Config) -> None:
        assert config.get_pricing("nonexistent-model") is None

    def test_is_initialized_true(self, config: Config) -> None:
        assert config.is_initialized() is True

    def test_is_initialized_false(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
        monkeypatch.chdir(tmp_path)
        cfg = Config()
        assert cfg.is_initialized() is False

    def test_default_config_has_providers(self) -> None:
        assert "openai" in DEFAULT_CONFIG["providers"]
        assert "anthropic" in DEFAULT_CONFIG["providers"]
