"""Tests for FastAPI API endpoints."""

import json
from unittest.mock import MagicMock, patch

import pytest
from fastapi.testclient import TestClient

from api.main import app

client = TestClient(app)


def _mock_openai_client():
    return MagicMock()


# Override the dependency for all tests in this module
app.dependency_overrides = {}


@pytest.fixture(autouse=True)
def override_openai_dep():
    """Override OpenAI client dependency for all API tests."""
    from api.dependencies import get_openai_client

    app.dependency_overrides[get_openai_client] = _mock_openai_client
    yield
    app.dependency_overrides.clear()


class TestHealthEndpoint:
    def test_get_health_returns_200(self):
        response = client.get("/health")
        assert response.status_code == 200
        assert response.json() == {"status": "ok"}


class TestIndexerEndpoint:
    @patch("api.routers.indexer.process_record")
    def test_post_indexer_run(self, mock_process):
        from unittest.mock import AsyncMock

        mock_process.side_effect = AsyncMock(
            return_value=(
                {"ID": "1", "indexing_status": "complete", "study_type": "RCT"},
                {"input_tokens": 100, "output_tokens": 50, "total_tokens": 150},
                None,
            )
        )
        response = client.post(
            "/indexer/run",
            json={
                "model": "gpt-5-mini",
                "records": [{"ID": "1", "Title": "Study A", "Abstract": "Abstract A"}],
                "fields": [
                    {
                        "name": "study_type",
                        "description": "Type of study",
                        "data_type_primary": "string",
                        "data_type_secondary": "NA",
                    },
                ],
                "batch_size": 50,
                "max_workers": 1,
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert "results" in data
        assert "errors" in data
        assert "usage" in data


class TestIndexerRefineFieldsEndpoint:
    """Baseline tests for POST /indexer/refine-fields."""

    def test_refine_fields_returns_suggestions(self):
        """Mock the LLM response to verify the parsing pipeline."""
        mock_response = MagicMock()
        mock_response.choices = [MagicMock()]
        mock_response.choices[0].message.content = json.dumps(
            [
                {
                    "action": "modify",
                    "field": {
                        "name": "study_type",
                        "description": "Improved description",
                        "data_type_primary": "string",
                    },
                    "rationale": "More specific description helps extraction",
                }
            ]
        )

        mock_client = _mock_openai_client()
        mock_client.chat.completions.create.return_value = mock_response

        from api.dependencies import get_openai_client

        app.dependency_overrides[get_openai_client] = lambda: mock_client

        response = client.post(
            "/indexer/refine-fields",
            json={
                "fields": [
                    {"name": "study_type", "description": "Type", "data_type_primary": "string"},
                ],
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert "suggestions" in data
        assert isinstance(data["suggestions"], list)
        assert len(data["suggestions"]) == 1
        assert data["suggestions"][0]["action"] == "modify"

    def test_refine_fields_handles_empty_response(self):
        """Verify graceful handling when LLM returns no suggestions."""
        mock_response = MagicMock()
        mock_response.choices = [MagicMock()]
        mock_response.choices[0].message.content = "[]"

        mock_client = _mock_openai_client()
        mock_client.chat.completions.create.return_value = mock_response

        from api.dependencies import get_openai_client

        app.dependency_overrides[get_openai_client] = lambda: mock_client

        response = client.post(
            "/indexer/refine-fields",
            json={
                "fields": [
                    {"name": "study_type", "description": "Type", "data_type_primary": "string"},
                ],
            },
        )
        assert response.status_code == 200
        assert response.json()["suggestions"] == []

    def test_refine_fields_handles_malformed_json(self):
        """Verify graceful handling when LLM returns invalid JSON."""
        mock_response = MagicMock()
        mock_response.choices = [MagicMock()]
        mock_response.choices[0].message.content = "I cannot provide suggestions in JSON format"

        mock_client = _mock_openai_client()
        mock_client.chat.completions.create.return_value = mock_response

        from api.dependencies import get_openai_client

        app.dependency_overrides[get_openai_client] = lambda: mock_client

        response = client.post(
            "/indexer/refine-fields",
            json={
                "fields": [
                    {"name": "study_type", "description": "Type", "data_type_primary": "string"},
                ],
            },
        )
        assert response.status_code == 200
        assert response.json()["suggestions"] == []


class TestIndexerGroupTagsEndpoint:
    """Baseline tests for POST /indexer/group-tags."""

    def test_group_tags_returns_groups(self):
        mock_response = MagicMock()
        mock_response.choices = [MagicMock()]
        mock_response.choices[0].message.content = json.dumps(
            {
                "groups": [
                    {"name": "Randomized", "values": ["RCT", "randomized trial"], "rationale": "Same concept"},
                    {"name": "Observational", "values": ["cohort", "case-control"], "rationale": "Non-experimental"},
                ]
            }
        )

        mock_client = _mock_openai_client()
        mock_client.chat.completions.create.return_value = mock_response

        from api.dependencies import get_openai_client

        app.dependency_overrides[get_openai_client] = lambda: mock_client

        response = client.post(
            "/indexer/group-tags",
            json={
                "field_name": "study_type",
                "values": ["RCT", "randomized trial", "cohort", "case-control"],
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert "groups" in data
        assert len(data["groups"]) == 2
        assert data["groups"][0]["name"] == "Randomized"

    def test_group_tags_handles_empty_values(self):
        mock_response = MagicMock()
        mock_response.choices = [MagicMock()]
        mock_response.choices[0].message.content = '{"groups": []}'

        mock_client = _mock_openai_client()
        mock_client.chat.completions.create.return_value = mock_response

        from api.dependencies import get_openai_client

        app.dependency_overrides[get_openai_client] = lambda: mock_client

        response = client.post(
            "/indexer/group-tags",
            json={
                "field_name": "study_type",
                "values": [],
            },
        )
        assert response.status_code == 200
        assert response.json()["groups"] == []


class TestIndexerSuggestFieldsEndpoint:
    """Baseline tests for POST /indexer/suggest-fields."""

    def test_suggest_fields_with_mock(self):
        response = client.post(
            "/indexer/suggest-fields",
            json={
                "mock": True,
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert "fields" in data
        assert isinstance(data["fields"], list)
        assert len(data["fields"]) >= 3
        for field in data["fields"]:
            assert "name" in field
            assert "description" in field
            assert "data_type_primary" in field


class TestIndexerCostEstimateEndpoint:
    """Baseline tests for POST /indexer/estimate."""

    def test_estimate_returns_cost(self):
        response = client.post(
            "/indexer/estimate",
            json={
                "model": "gpt-5-mini",
                "fields": [
                    {"name": "study_type", "description": "Type", "data_type_primary": "string"},
                    {"name": "sample_size", "description": "N", "data_type_primary": "number"},
                ],
                "record_count": 100,
            },
        )
        assert response.status_code == 200
        data = response.json()
        assert "estimated_input_tokens" in data
        assert "estimated_output_tokens" in data
        assert "estimated_cost_usd" in data
        assert data["estimated_input_tokens"] > 0
        assert data["estimated_output_tokens"] > 0
        assert data["estimated_cost_usd"] > 0
