from __future__ import annotations

from datetime import datetime, timezone
from types import SimpleNamespace
from unittest.mock import MagicMock

import pytest

from witty_service.api import models as models_api
from witty_service.api.schemas import CreateModelRequest, UpdateModelRequest
from witty_service.domain.errors import DomainError
from witty_service.persistence.repositories import ModelRecord


def _model_record(**overrides: object) -> ModelRecord:
    now = datetime.now(timezone.utc)
    data = {
        "id": "model-1",
        "name": "GPT",
        "provider": "openai",
        "compatibility": None,
        "api_key": "secret",
        "api_base_url": "https://api.openai.com/v1",
        "enabled": True,
        "max_tokens": 4096,
        "temperature": 0.7,
        "is_default": False,
        "created_at": now,
        "updated_at": now,
    }
    data.update(overrides)
    return ModelRecord(**data)


def _services() -> MagicMock:
    services = MagicMock()
    services.repository = MagicMock()
    return services


def test_create_model_uses_provider_default_api_base_url() -> None:
    services = _services()
    services.repository.create_model.return_value = _model_record()

    resp = models_api.create_model(
        payload=CreateModelRequest(
            name="GPT",
            provider="openai",
            api_key="secret",
        ),
        services=services,
    )

    services.repository.create_model.assert_called_once_with(
        name="GPT",
        provider="openai",
        api_key="secret",
        api_base_url="https://api.openai.com/v1",
        compatibility=None,
        enabled=True,
        max_tokens=4096,
        temperature=0.7,
        is_default=False,
    )
    assert resp.id == "model-1"
    assert resp.api_base_url == "https://api.openai.com/v1"


def test_list_models_returns_model_responses() -> None:
    services = _services()
    services.repository.list_models.return_value = [_model_record(name="GPT 4")]

    resp = models_api.list_models(services=services)

    assert len(resp) == 1
    assert resp[0].name == "GPT 4"


def test_delete_model_raises_domain_error_when_missing() -> None:
    services = _services()
    services.repository.get_model.return_value = None

    with pytest.raises(DomainError) as exc_info:
        models_api.delete_model("missing", services=services)

    assert exc_info.value.code == models_api.MODEL_NOT_FOUND
    assert exc_info.value.details == {"model_id": "missing"}


def test_delete_model_removes_existing_model() -> None:
    services = _services()
    services.repository.get_model.return_value = _model_record()

    resp = models_api.delete_model("model-1", services=services)

    assert resp.status_code == 204
    services.repository.delete_model.assert_called_once_with("model-1")


def test_update_model_uses_provider_default_and_returns_response() -> None:
    services = _services()
    services.repository.get_model.return_value = _model_record()
    services.repository.update_model.return_value = _model_record(
        provider="anthropic",
        api_base_url="https://api.anthropic.com/v1",
        enabled=False,
    )

    resp = models_api.update_model(
        "model-1",
        payload=UpdateModelRequest(provider="anthropic", enabled=False),
        services=services,
    )

    services.repository.update_model.assert_called_once_with(
        model_id="model-1",
        name=None,
        provider="anthropic",
        api_key=None,
        api_base_url="https://api.anthropic.com/v1",
        compatibility=None,
        enabled=False,
        max_tokens=None,
        temperature=None,
        is_default=None,
    )
    assert resp.provider == "anthropic"
    assert resp.enabled is False


def test_update_model_raises_when_missing() -> None:
    services = _services()
    services.repository.get_model.return_value = None

    with pytest.raises(DomainError) as exc_info:
        models_api.update_model(
            "missing",
            payload=UpdateModelRequest(name="new-name"),
            services=services,
        )

    assert exc_info.value.code == models_api.MODEL_NOT_FOUND