import json
import pytest
from pathlib import Path
from unittest.mock import MagicMock, patch
from transduck.config import TransduckConfig
from transduck.providers.prompts import build_messages, build_plural_messages, build_single_prompt, build_plural_single_prompt


def _make_config(**overrides):
    defaults = dict(
        project_name="test", project_context="test",
        source_lang="EN", target_langs=["DE"],
        storage_path=Path("/tmp/test.db"),
        provider="openai", api_key_env="OPENAI_API_KEY", token_env="",
        backend_model="gpt-4.1-mini", backend_timeout=10, backend_max_retries=2,
        shared_url=None, read_only=False,
    )
    defaults.update(overrides)
    return TransduckConfig(**defaults)


# --- Prompt tests ---

def test_build_messages():
    messages = build_messages("Hello", "EN", "DE", "A travel site", "greeting")
    assert len(messages) == 2
    assert messages[0]["role"] == "system"
    assert "EN" in messages[0]["content"]
    assert "DE" in messages[0]["content"]
    assert messages[1]["role"] == "user"
    assert "Hello" in messages[1]["content"]


def test_build_single_prompt():
    prompt = build_single_prompt("Hello", "EN", "DE", "A travel site", "greeting")
    assert "EN" in prompt
    assert "DE" in prompt
    assert "Hello" in prompt
    assert "greeting" in prompt


def test_build_plural_messages():
    messages = build_plural_messages("{count} item", "{count} items", "EN", "DE", "test", None)
    assert len(messages) == 2
    assert "CLDR" in messages[0]["content"]


def test_build_plural_single_prompt():
    prompt = build_plural_single_prompt("{count} item", "{count} items", "EN", "DE", "test", None)
    assert "CLDR" in prompt
    assert "{count} item" in prompt


# --- OpenAI provider tests ---

@patch("transduck.providers.openai_provider.openai")
def test_openai_translate(mock_openai):
    from transduck.providers.openai_provider import translate

    mock_client = MagicMock()
    mock_openai.OpenAI.return_value = mock_client
    mock_client.chat.completions.create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(content="Hallo"))]
    )

    cfg = _make_config()
    with patch.dict("os.environ", {"OPENAI_API_KEY": "test-key"}):
        result = translate("Hello", "EN", "DE", "test", None, cfg)
    assert result == "Hallo"


@patch("transduck.providers.openai_provider.openai")
def test_openai_translate_plural(mock_openai):
    from transduck.providers.openai_provider import translate_plural

    mock_client = MagicMock()
    mock_openai.OpenAI.return_value = mock_client
    response_json = json.dumps({"one": "{count} Nachricht", "other": "{count} Nachrichten"})
    mock_client.chat.completions.create.return_value = MagicMock(
        choices=[MagicMock(message=MagicMock(content=response_json))]
    )

    cfg = _make_config()
    with patch.dict("os.environ", {"OPENAI_API_KEY": "test-key"}):
        result = translate_plural("{count} message", "{count} messages", "EN", "DE", "test", None, cfg)
    assert result == {"one": "{count} Nachricht", "other": "{count} Nachrichten"}


# --- Router tests ---

def test_get_provider_openai():
    from transduck.providers import get_provider
    from transduck.providers import openai_provider

    cfg = _make_config(provider="openai")
    provider = get_provider(cfg)
    assert provider is openai_provider


def test_get_provider_claude_api():
    from transduck.providers import get_provider
    from transduck.providers import claude_api

    cfg = _make_config(provider="claude_api")
    provider = get_provider(cfg)
    assert provider is claude_api


def test_get_provider_claude_code():
    from transduck.providers import get_provider
    from transduck.providers import claude_code

    cfg = _make_config(provider="claude_code")
    provider = get_provider(cfg)
    assert provider is claude_code


def test_get_provider_invalid():
    from transduck.providers import get_provider

    cfg = _make_config(provider="invalid")
    with pytest.raises(ValueError, match="Unknown provider"):
        get_provider(cfg)


# --- Claude API provider tests ---

def test_claude_api_translate():
    import sys
    from transduck.providers.claude_api import translate

    mock_anthropic = MagicMock()
    mock_client = MagicMock()
    mock_anthropic.Anthropic.return_value = mock_client
    mock_client.messages.create.return_value = MagicMock(
        content=[MagicMock(text="Hallo")]
    )

    cfg = _make_config(
        provider="claude_api",
        api_key_env="ANTHROPIC_API_KEY",
        backend_model="claude-haiku-4-5-20251001",
    )
    with patch.dict(sys.modules, {"anthropic": mock_anthropic}):
        with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "test-key"}):
            result = translate("Hello", "EN", "DE", "test", None, cfg)
    assert result == "Hallo"

    # Verify system param is separate from messages (Anthropic API pattern)
    call_kwargs = mock_client.messages.create.call_args
    assert "system" in call_kwargs.kwargs
    assert call_kwargs.kwargs["messages"][0]["role"] == "user"


def test_claude_api_translate_plural():
    import sys
    from transduck.providers.claude_api import translate_plural

    mock_anthropic = MagicMock()
    mock_client = MagicMock()
    mock_anthropic.Anthropic.return_value = mock_client
    response_json = json.dumps({"one": "{count} Nachricht", "other": "{count} Nachrichten"})
    mock_client.messages.create.return_value = MagicMock(
        content=[MagicMock(text=response_json)]
    )

    cfg = _make_config(
        provider="claude_api",
        api_key_env="ANTHROPIC_API_KEY",
        backend_model="claude-haiku-4-5-20251001",
    )
    with patch.dict(sys.modules, {"anthropic": mock_anthropic}):
        with patch.dict("os.environ", {"ANTHROPIC_API_KEY": "test-key"}):
            result = translate_plural("{count} message", "{count} messages", "EN", "DE", "test", None, cfg)
    assert result == {"one": "{count} Nachricht", "other": "{count} Nachrichten"}


def test_claude_api_missing_key():
    import sys
    from transduck.providers.claude_api import translate

    mock_anthropic = MagicMock()

    cfg = _make_config(
        provider="claude_api",
        api_key_env="ANTHROPIC_API_KEY",
        backend_model="claude-haiku-4-5-20251001",
    )
    with patch.dict(sys.modules, {"anthropic": mock_anthropic}):
        with patch.dict("os.environ", {}, clear=True):
            with pytest.raises(RuntimeError, match="Set ANTHROPIC_API_KEY"):
                translate("Hello", "EN", "DE", "test", None, cfg)


# --- Claude Code provider tests ---

def test_claude_code_translate():
    from transduck.providers.claude_code import translate

    async def fake_query(**kwargs):
        class Result:
            result = "Hallo"
        yield Result()

    cfg = _make_config(
        provider="claude_code",
        token_env="CLAUDE_CODE_OAUTH_TOKEN",
        read_only=True,
    )
    with patch.dict("os.environ", {"CLAUDE_CODE_OAUTH_TOKEN": "test-token"}):
        with patch("transduck.providers.claude_code.query", side_effect=fake_query, create=True) as mock_q, \
             patch("transduck.providers.claude_code.ClaudeAgentOptions", create=True):
            # Patch the imports inside _translate_async
            import transduck.providers.claude_code as mod
            original_fn = mod._translate_async

            async def patched_translate_async(prompt):
                class Result:
                    result = "Hallo"
                return Result.result

            mod._translate_async = patched_translate_async
            try:
                result = translate("Hello", "EN", "DE", "test", None, cfg)
            finally:
                mod._translate_async = original_fn
    assert result == "Hallo"


def test_claude_code_translate_plural():
    from transduck.providers.claude_code import translate_plural
    import transduck.providers.claude_code as mod

    response_json = json.dumps({"one": "{count} Nachricht", "other": "{count} Nachrichten"})
    original_fn = mod._translate_async

    async def patched_translate_async(prompt):
        return response_json

    cfg = _make_config(
        provider="claude_code",
        token_env="CLAUDE_CODE_OAUTH_TOKEN",
        read_only=True,
    )
    mod._translate_async = patched_translate_async
    try:
        with patch.dict("os.environ", {"CLAUDE_CODE_OAUTH_TOKEN": "test-token"}):
            result = translate_plural("{count} message", "{count} messages", "EN", "DE", "test", None, cfg)
    finally:
        mod._translate_async = original_fn
    assert result == {"one": "{count} Nachricht", "other": "{count} Nachrichten"}


def test_claude_code_missing_token():
    from transduck.providers.claude_code import translate

    cfg = _make_config(
        provider="claude_code",
        token_env="CLAUDE_CODE_OAUTH_TOKEN",
        read_only=True,
    )
    with patch.dict("os.environ", {}, clear=True):
        with pytest.raises(RuntimeError, match="Set CLAUDE_CODE_OAUTH_TOKEN"):
            translate("Hello", "EN", "DE", "test", None, cfg)
