import json
import pytest
from pathlib import Path
from click.testing import CliRunner
from unittest.mock import patch, MagicMock
from transduck.cli import main


@pytest.fixture
def runner():
    return CliRunner()


@pytest.fixture
def project_dir(tmp_path):
    return tmp_path


def test_init_creates_config_and_db(runner, project_dir):
    result = runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE,ES\n1\n")
    assert result.exit_code == 0
    assert (project_dir / "transduck.yaml").exists()
    assert (project_dir / "translations.db").exists()


def test_stats_empty_db(runner, project_dir):
    # First init
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE,ES\n1\n")
    result = runner.invoke(main, ["stats", "--config", str(project_dir / "transduck.yaml")])
    assert result.exit_code == 0
    assert "0" in result.output


@patch("transduck.backend.translate")
def test_translate_command(mock_translate, runner, project_dir):
    mock_translate.return_value = "Hallo"
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE,ES\n1\n")
    result = runner.invoke(main, [
        "translate", "Hello",
        "--to", "DE",
        "--config", str(project_dir / "transduck.yaml"),
    ], env={"OPENAI_API_KEY": "test-key"})
    assert result.exit_code == 0
    assert "Hallo" in result.output
    assert "[new]" in result.output


@patch("transduck.backend.translate")
def test_translate_cached(mock_translate, runner, project_dir):
    mock_translate.return_value = "Hallo"
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE,ES\n1\n")
    config_path = str(project_dir / "transduck.yaml")
    env = {"OPENAI_API_KEY": "test-key"}
    runner.invoke(main, ["translate", "Hello", "--to", "DE", "--config", config_path], env=env)
    mock_translate.reset_mock()
    result = runner.invoke(main, ["translate", "Hello", "--to", "DE", "--config", config_path], env=env)
    assert "[cached]" in result.output
    mock_translate.assert_not_called()


@patch("transduck.backend.translate")
def test_warm_command(mock_translate, runner, project_dir):
    mock_translate.return_value = "translated"
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    warm_file = project_dir / "strings.json"
    warm_file.write_text(json.dumps([{"text": "Hello"}, {"text": "Goodbye"}]))
    result = runner.invoke(main, [
        "warm",
        "--file", str(warm_file),
        "--langs", "DE",
        "--config", str(project_dir / "transduck.yaml"),
    ], env={"OPENAI_API_KEY": "test-key"})
    assert result.exit_code == 0
    assert "Translated: 2" in result.output


@patch("transduck.backend.translate")
def test_warm_command_text_file(mock_translate, runner, project_dir):
    mock_translate.return_value = "translated"
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    warm_file = project_dir / "strings.txt"
    warm_file.write_text("Hello\nGoodbye\n")
    result = runner.invoke(main, [
        "warm",
        "--file", str(warm_file),
        "--langs", "DE",
        "--config", str(project_dir / "transduck.yaml"),
    ], env={"OPENAI_API_KEY": "test-key"})
    assert result.exit_code == 0
    assert "Translated: 2" in result.output


@patch("transduck.backend.translate")
def test_translate_with_vars(mock_translate, runner, project_dir):
    mock_translate.return_value = "Willkommen {name}"
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    result = runner.invoke(main, [
        "translate", "Welcome {name}",
        "--to", "DE",
        "--vars", '{"name": "Tim"}',
        "--config", str(project_dir / "transduck.yaml"),
    ], env={"OPENAI_API_KEY": "test-key"})
    assert result.exit_code == 0
    assert "[new] Willkommen Tim" in result.output


@patch("transduck.backend.translate")
def test_translate_with_vars_cached(mock_translate, runner, project_dir):
    mock_translate.return_value = "Willkommen {name}"
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    config_path = str(project_dir / "transduck.yaml")
    env = {"OPENAI_API_KEY": "test-key"}
    # First call populates cache
    runner.invoke(main, [
        "translate", "Welcome {name}", "--to", "DE",
        "--vars", '{"name": "Tim"}', "--config", config_path,
    ], env=env)
    mock_translate.reset_mock()
    # Second call should be cached, with vars still interpolated
    result = runner.invoke(main, [
        "translate", "Welcome {name}", "--to", "DE",
        "--vars", '{"name": "Alice"}', "--config", config_path,
    ], env=env)
    assert "[cached] Willkommen Alice" in result.output
    mock_translate.assert_not_called()


@patch("transduck.backend.translate_plural")
def test_translate_plural_command(mock_translate_plural, runner, project_dir):
    mock_translate_plural.return_value = {
        "one": "{count} Nachricht",
        "other": "{count} Nachrichten",
    }
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    result = runner.invoke(main, [
        "translate-plural",
        "--one", "{count} message",
        "--other", "{count} messages",
        "--count", "5",
        "--to", "DE",
        "--config", str(project_dir / "transduck.yaml"),
    ], env={"OPENAI_API_KEY": "test-key"})
    assert result.exit_code == 0
    assert "[new] 5 Nachrichten" in result.output


@patch("transduck.backend.translate_plural")
def test_translate_plural_cached(mock_translate_plural, runner, project_dir):
    mock_translate_plural.return_value = {
        "one": "{count} Nachricht",
        "other": "{count} Nachrichten",
    }
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    config_path = str(project_dir / "transduck.yaml")
    env = {"OPENAI_API_KEY": "test-key"}
    # First call populates cache
    runner.invoke(main, [
        "translate-plural", "--one", "{count} message", "--other", "{count} messages",
        "--count", "5", "--to", "DE", "--config", config_path,
    ], env=env)
    mock_translate_plural.reset_mock()
    # Second call with count=1 should hit cache (singular form)
    result = runner.invoke(main, [
        "translate-plural", "--one", "{count} message", "--other", "{count} messages",
        "--count", "1", "--to", "DE", "--config", config_path,
    ], env=env)
    assert "[cached] 1 Nachricht" in result.output
    mock_translate_plural.assert_not_called()


@patch("transduck.backend.translate")
def test_scan_finds_strings(mock_translate, runner, project_dir):
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE,ES\n1\n")
    # Create a source file to scan
    src = project_dir / "app.py"
    src.write_text('from transduck import ait\nait("Hello")\nait("World", context="greeting")\n')
    result = runner.invoke(main, ["scan", "--dir", str(project_dir), "--config", str(project_dir / "transduck.yaml")])
    assert result.exit_code == 0
    assert "Hello" in result.output
    assert "World" in result.output
    assert "2" in result.output  # found 2 strings


def test_scan_output_json(runner, project_dir):
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    src = project_dir / "app.py"
    src.write_text('ait("Hello")\n')
    output_file = project_dir / "strings.json"
    result = runner.invoke(main, ["scan", "--dir", str(project_dir), "--output", str(output_file), "--config", str(project_dir / "transduck.yaml")])
    assert result.exit_code == 0
    assert output_file.exists()
    data = json.loads(output_file.read_text())
    assert len(data) == 1
    assert data[0]["text"] == "Hello"


@patch("transduck.backend.translate")
def test_scan_warm(mock_translate, runner, project_dir):
    mock_translate.return_value = "Hallo"
    runner.invoke(main, ["init"], input=f"{project_dir}\ntest-project\nA test site\nEN\nDE\n1\n")
    src = project_dir / "app.py"
    src.write_text('ait("Hello")\n')
    result = runner.invoke(main, [
        "scan", "--dir", str(project_dir), "--warm", "--langs", "DE",
        "--config", str(project_dir / "transduck.yaml"),
    ], env={"OPENAI_API_KEY": "test-key"})
    assert result.exit_code == 0
    assert "Translated: 1" in result.output
