from types import SimpleNamespace

import pytest

from app.services import llm_client


class _DummyModel:
    def __init__(self, candidate):
        self._candidate = candidate

    def generate_content(self, *args, **kwargs):  # pragma: no cover - trivial stub
        return SimpleNamespace(candidates=[self._candidate])


def _candidate(text=None, finish_reason=None, safety=None):
    content = None
    if text is None:
        content = SimpleNamespace(parts=[SimpleNamespace(text='{"title": "Test", "description": "Desc"}')])
    return SimpleNamespace(
        text=text,
        output_text=text,
        finish_reason=finish_reason,
        safety_ratings=safety or [],
        content=content,
    )


def test_invoke_chat_parses_json(monkeypatch):
    candidate = _candidate(text='{"title": "Example", "description": "Sample"}')
    monkeypatch.setattr(llm_client, "_get_model", lambda: _DummyModel(candidate))
    result = llm_client.invoke_chat([
        {"role": "user", "content": "hello"},
    ])
    assert result["title"] == "Example"
    assert result["description"] == "Sample"


def test_invoke_chat_raises_on_safety(monkeypatch):
    rating = SimpleNamespace(category=SimpleNamespace(name="HARM_CATEGORY_SEXUALLY_EXPLICIT"), blocked=True)
    candidate = _candidate(text=None, finish_reason=llm_client.FinishReason.SAFETY, safety=[rating])
    monkeypatch.setattr(llm_client, "_get_model", lambda: _DummyModel(candidate))

    with pytest.raises(llm_client.LLMContentFilteredError) as exc:
        llm_client.invoke_chat([
            {"role": "user", "content": "hello"},
        ])

    assert exc.value.category == "HARM_CATEGORY_SEXUALLY_EXPLICIT"


def test_invoke_chat_accepts_response_schema(monkeypatch):
    class _SchemaModel:
        def generate_content(self, *args, **kwargs):
            return SimpleNamespace(candidates=[_candidate(text='{"overallSafe": true, "checks": []}')])

    monkeypatch.setattr(llm_client, "_get_model", lambda: _SchemaModel())

    schema = {
        "type": "object",
        "properties": {
            "overallSafe": {"type": "boolean"},
            "checks": {"type": "array"},
        },
        "required": ["overallSafe"],
    }

    result = llm_client.invoke_chat([
        {"role": "user", "content": "hi"},
    ], response_schema=schema)

    assert result["overallSafe"] is True


def test_invoke_chat_schema_validation_failure(monkeypatch):
    class _SchemaModel:
        def generate_content(self, *args, **kwargs):
            return SimpleNamespace(candidates=[_candidate(text='{"checks": []}')])

    monkeypatch.setattr(llm_client, "_get_model", lambda: _SchemaModel())

    schema = {
        "type": "object",
        "properties": {
            "overallSafe": {"type": "boolean"},
        },
        "required": ["overallSafe"],
    }

    with pytest.raises(ValueError):
        llm_client.invoke_chat([
            {"role": "user", "content": "hi"},
        ], response_schema=schema)
