import hashlib
import pytest
from pathlib import Path
from unittest.mock import patch, MagicMock
import transduck
from transduck import ait, set_language
from transduck.config import TransduckConfig


def _hash(text: str) -> str:
    return hashlib.sha256(text.encode()).hexdigest()


@pytest.fixture
def mock_config(tmp_path):
    return TransduckConfig(
        project_name="test",
        project_context="A test site",
        source_lang="EN",
        target_langs=["DE", "ES"],
        storage_path=tmp_path / "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,
    )


@pytest.fixture(autouse=True)
def reset_state():
    """Reset global state between tests."""
    transduck._state = transduck._State()
    yield
    transduck._wait_for_background(timeout=2.0)
    transduck._state = transduck._State()


@pytest.fixture
def initialized(mock_config, monkeypatch):
    monkeypatch.setenv("OPENAI_API_KEY", "test-key")
    transduck.initialize(mock_config)
    set_language("DE")


def test_ait_returns_source_when_same_language(initialized):
    set_language("EN")
    assert ait("Hello") == "Hello"


def test_ait_cache_hit(initialized):
    # Pre-insert a translation
    transduck._state.store.insert(
        source_text="Hello",
        source_lang="EN",
        target_lang="DE",
        project_context_hash=_hash("A test site"),
        string_context_hash=_hash(""),
        translated_text="Hallo",
        model="gpt-4.1-mini",
        string_context="",
    )
    assert ait("Hello") == "Hallo"


@patch("transduck.backend.translate")
def test_ait_cache_miss_calls_backend(mock_translate, initialized):
    mock_translate.return_value = "Hallo"
    result = ait("Hello")
    assert result == "Hallo"
    mock_translate.assert_called_once()


@patch("transduck.backend.translate")
def test_ait_caches_after_miss(mock_translate, initialized):
    mock_translate.return_value = "Hallo"
    ait("Hello")
    # Second call should hit cache, not backend
    mock_translate.reset_mock()
    result = ait("Hello")
    assert result == "Hallo"
    mock_translate.assert_not_called()


@patch("transduck.backend.translate")
def test_ait_returns_source_on_backend_failure(mock_translate, initialized):
    mock_translate.side_effect = Exception("API down")
    result = ait("Hello")
    assert result == "Hello"


@patch("transduck.backend.translate")
def test_ait_returns_source_on_validation_failure(mock_translate, initialized):
    mock_translate.return_value = "Hallo"  # missing {name} placeholder
    result = ait("Hello {name}")
    assert result == "Hello {name}"


@patch("transduck.backend.translate")
def test_ait_with_context(mock_translate, initialized):
    mock_translate.return_value = "Unsere Veranstaltungen"
    result = ait("Our Events", context="Events are concerts")
    assert result == "Unsere Veranstaltungen"
    call_kwargs = mock_translate.call_args
    assert call_kwargs[1]["string_context"] == "Events are concerts" or \
           call_kwargs.kwargs.get("string_context") == "Events are concerts"


def test_set_language_normalizes():
    set_language("de")
    assert transduck._state.target_lang == "DE"


def test_ait_raises_if_not_initialized():
    with pytest.raises(RuntimeError):
        ait("Hello")


def test_ait_raises_if_no_language(mock_config, monkeypatch):
    monkeypatch.setenv("OPENAI_API_KEY", "test-key")
    transduck.initialize(mock_config)
    # Language not set — should raise
    with pytest.raises(RuntimeError, match="Target language not set"):
        ait("Hello")


@patch("transduck.backend.translate")
def test_ait_with_vars(mock_translate, initialized):
    mock_translate.return_value = "Willkommen {name}"
    result = ait("Welcome {name}", vars={"name": "Tim"})
    assert result == "Willkommen Tim"


@patch("transduck.backend.translate")
def test_ait_vars_from_cache(mock_translate, initialized):
    mock_translate.return_value = "Willkommen {name}"
    ait("Welcome {name}", vars={"name": "Tim"})
    mock_translate.reset_mock()
    result = ait("Welcome {name}", vars={"name": "Anna"})
    assert result == "Willkommen Anna"
    mock_translate.assert_not_called()


def test_ait_vars_with_same_language(initialized):
    set_language("EN")
    result = ait("Welcome {name}", vars={"name": "Tim"})
    assert result == "Welcome Tim"


def test_ait_no_vars_backward_compat(initialized):
    """Existing calls without vars still work."""
    transduck._state.store.insert(
        source_text="Hello", source_lang="EN", target_lang="DE",
        project_context_hash=_hash("A test site"), string_context_hash=_hash(""),
        translated_text="Hallo", model="gpt-4.1-mini",
        string_context="",
    )
    assert ait("Hello") == "Hallo"
