"""Tests for onboarding wizard and chat command."""

from pathlib import Path
from unittest.mock import patch

import yaml

from agentkit.cli.onboarding import (
    PROVIDER_PRESETS,
    needs_onboarding,
    run_onboarding,
)


class TestNeedsOnboarding:
    def test_needs_onboarding_when_no_config(self, tmp_path, monkeypatch):
        """Should return True when no config file exists."""
        monkeypatch.chdir(tmp_path)
        monkeypatch.delenv("AGENTKIT_CONFIG_PATH", raising=False)
        assert needs_onboarding() is True

    def test_no_onboarding_when_config_exists(self, tmp_path, monkeypatch):
        """Should return False when agentkit.yaml exists."""
        config_file = tmp_path / "agentkit.yaml"
        config_file.write_text("server:\n  port: 8001\n")
        monkeypatch.chdir(tmp_path)
        assert needs_onboarding(config_arg=str(config_file)) is False

    def test_no_onboarding_with_home_config(self, tmp_path, monkeypatch):
        """Should return False when ~/.agentkit/agentkit.yaml exists."""
        home_dir = tmp_path / "home"
        home_dir.mkdir()
        agentkit_dir = home_dir / ".agentkit"
        agentkit_dir.mkdir()
        (agentkit_dir / "agentkit.yaml").write_text("server:\n  port: 8001\n")
        monkeypatch.setenv("HOME", str(home_dir))
        monkeypatch.chdir(tmp_path / "empty" if (tmp_path / "empty").exists() else tmp_path)
        # Create empty cwd to ensure no local config
        empty_dir = tmp_path / "empty"
        empty_dir.mkdir()
        monkeypatch.chdir(empty_dir)
        assert needs_onboarding() is False


class TestProviderPresets:
    def test_all_presets_have_required_fields(self):
        """Every provider preset must have name, env_key, base_url, models."""
        for key, preset in PROVIDER_PRESETS.items():
            assert "name" in preset, f"{key} missing name"
            assert "env_key" in preset, f"{key} missing env_key"
            assert "base_url" in preset, f"{key} missing base_url"
            assert "models" in preset, f"{key} missing models"
            assert preset["models"], f"{key} has empty models"
            assert "default_model" in preset, f"{key} missing default_model"

    def test_preset_keys_are_lowercase(self):
        """Provider keys should be lowercase."""
        for key in PROVIDER_PRESETS:
            assert key == key.lower(), f"Provider key '{key}' should be lowercase"

    def test_deepseek_preset(self):
        """DeepSeek preset should have correct configuration."""
        ds = PROVIDER_PRESETS["deepseek"]
        assert ds["env_key"] == "DEEPSEEK_API_KEY"
        assert "deepseek-chat" in ds["models"]
        assert ds["type"] == "openai"

    def test_qwen_preset(self):
        """Qwen preset should use DashScope endpoint."""
        qwen = PROVIDER_PRESETS["qwen"]
        assert "dashscope" in qwen["base_url"]
        assert qwen["env_key"] == "DASHSCOPE_API_KEY"


class TestRunOnboarding:
    def test_onboarding_generates_config_files(self, tmp_path, monkeypatch):
        """Onboarding should generate agentkit.yaml and .env."""
        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("HOME", str(tmp_path / "home"))
        (tmp_path / "home").mkdir(exist_ok=True)

        # Mock user input
        with patch("agentkit.cli.onboarding.Prompt") as mock_prompt, \
             patch("agentkit.cli.onboarding.Confirm") as mock_confirm:
            # Step 1: Select DeepSeek (option 1)
            # Step 2: API key
            # Step 2b: Select model (1 = deepseek-chat, default)
            # Step 5: Agent personality (name, personality, speaking_style)
            mock_prompt.ask.side_effect = ["1", "sk-test-deepseek-key", "1", "小王", "友好耐心", "简洁专业"]
            # Step 3: No second provider
            mock_confirm.ask.return_value = False

            config_path = run_onboarding(output_dir=str(tmp_path))

        assert config_path is not None
        assert Path(config_path).exists()

        # Verify agentkit.yaml content
        with open(config_path) as f:
            config = yaml.safe_load(f)
        assert "llm" in config
        assert "deepseek" in config["llm"]["providers"]
        assert config["llm"]["providers"]["deepseek"]["base_url"] == "https://api.deepseek.com/v1"

        # Verify .env content
        env_path = tmp_path / ".env"
        assert env_path.exists()
        env_content = env_path.read_text()
        assert "DEEPSEEK_API_KEY=sk-test-deepseek-key" in env_content

    def test_onboarding_with_two_providers(self, tmp_path, monkeypatch):
        """Onboarding should support adding a second provider."""
        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("HOME", str(tmp_path / "home"))
        (tmp_path / "home").mkdir(exist_ok=True)

        with patch("agentkit.cli.onboarding.Prompt") as mock_prompt, \
             patch("agentkit.cli.onboarding.Confirm") as mock_confirm:
            # Select DeepSeek (1), API key, model (1), then Qwen as second
            # After removing deepseek, remaining = [openai, bailian-coding, qwen, doubao, gemini, anthropic]
            # qwen is at index 2, so option 3
            # Step 5: Agent personality defaults
            mock_prompt.ask.side_effect = ["1", "sk-deepseek", "1", "3", "sk-dashscope", "1", "AgentKit", "专业、友好、注重细节", "简洁清晰"]
            mock_confirm.ask.return_value = True

            config_path = run_onboarding(output_dir=str(tmp_path))

        with open(config_path) as f:
            config = yaml.safe_load(f)

        providers = config["llm"]["providers"]
        assert "deepseek" in providers
        assert "qwen" in providers

        env_path = tmp_path / ".env"
        env_content = env_path.read_text()
        assert "DEEPSEEK_API_KEY=sk-deepseek" in env_content
        assert "DASHSCOPE_API_KEY=sk-dashscope" in env_content

    def test_onboarding_cancelled_on_empty_api_key(self, tmp_path, monkeypatch):
        """Onboarding should return None if API key is empty."""
        monkeypatch.chdir(tmp_path)

        with patch("agentkit.cli.onboarding.Prompt") as mock_prompt:
            mock_prompt.ask.side_effect = ["1", ""]  # Empty API key

            result = run_onboarding(output_dir=str(tmp_path))

        assert result is None

    def test_onboarding_config_has_memory_backend(self, tmp_path, monkeypatch):
        """Generated config should use memory backends by default."""
        monkeypatch.chdir(tmp_path)
        monkeypatch.setenv("HOME", str(tmp_path / "home"))
        (tmp_path / "home").mkdir(exist_ok=True)

        with patch("agentkit.cli.onboarding.Prompt") as mock_prompt, \
             patch("agentkit.cli.onboarding.Confirm") as mock_confirm:
            mock_prompt.ask.side_effect = ["1", "sk-test-key", "1", "AgentKit", "专业、友好、注重细节", "简洁清晰"]
            mock_confirm.ask.return_value = False

            config_path = run_onboarding(output_dir=str(tmp_path))

        with open(config_path) as f:
            config = yaml.safe_load(f)

        assert config["session"]["backend"] == "memory"
        assert config["bus"]["backend"] == "memory"
        assert config["task_store"]["backend"] == "memory"


class TestOnboardingSoulGeneration:
    """U5: Onboarding 生成 SOUL.md 测试."""

    def test_onboarding_creates_soul_md(self, tmp_path, monkeypatch):
        """Onboarding should create SOUL.md with custom agent name."""
        monkeypatch.chdir(tmp_path)
        home_dir = tmp_path / "home"
        home_dir.mkdir(exist_ok=True)
        monkeypatch.setenv("HOME", str(home_dir))

        with patch("agentkit.cli.onboarding.Prompt") as mock_prompt, \
             patch("agentkit.cli.onboarding.Confirm") as mock_confirm:
            mock_prompt.ask.side_effect = ["1", "sk-test-key", "1", "小王", "友好耐心", "简洁专业"]
            mock_confirm.ask.return_value = False

            run_onboarding(output_dir=str(tmp_path))

        # Verify SOUL.md was created
        soul_path = home_dir / ".agentkit" / "SOUL.md"
        assert soul_path.exists()
        soul_content = soul_path.read_text(encoding="utf-8")
        assert "小王" in soul_content
        assert "友好耐心" in soul_content
        assert "简洁专业" in soul_content

    def test_onboarding_soul_with_defaults(self, tmp_path, monkeypatch):
        """Onboarding with default personality should create default SOUL.md."""
        monkeypatch.chdir(tmp_path)
        home_dir = tmp_path / "home"
        home_dir.mkdir(exist_ok=True)
        monkeypatch.setenv("HOME", str(home_dir))

        with patch("agentkit.cli.onboarding.Prompt") as mock_prompt, \
             patch("agentkit.cli.onboarding.Confirm") as mock_confirm:
            # Prompt.ask returns the default value when user presses Enter
            # Our mock needs to return the actual default values
            mock_prompt.ask.side_effect = ["1", "sk-test-key", "1", "AgentKit", "专业、友好、注重细节", "简洁清晰"]
            mock_confirm.ask.return_value = False

            run_onboarding(output_dir=str(tmp_path))

        soul_path = home_dir / ".agentkit" / "SOUL.md"
        assert soul_path.exists()
        soul_content = soul_path.read_text(encoding="utf-8")
        assert "AgentKit" in soul_content
