"""Tests for MetadataFields client."""

import pytest
from pytest_httpx import HTTPXMock

from concentriq import ConcentriqClient
from concentriq.auth import ApiKeyAuth


class TestMetadataFieldsList:
    """Tests for MetadataFields.list() behavior."""

    def test_list_returns_list_of_fields(self, httpx_mock: HTTPXMock):
        """List should return a list of metadata field definitions from nested response."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json={
                "meta": {"status": 200},
                "data": {
                    "fields": [
                        {"id": 1, "name": "Diagnosis", "contentType": "string"},
                        {"id": 2, "name": "Age", "contentType": "number"},
                    ]
                },
            },
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.metadata_fields.list()

        assert isinstance(result, list)
        assert len(result) == 2
        assert result[0]["id"] == 1
        assert result[0]["name"] == "Diagnosis"

    def test_list_does_not_send_pagination_params(self, httpx_mock: HTTPXMock):
        """List should not send pagination params (API has no pagination)."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json={"meta": {"status": 200}, "data": {"fields": []}},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        client.v1.metadata_fields.list()

        request = httpx_mock.get_request()
        assert "page" not in str(request.url)
        assert "rowsPerPage" not in str(request.url)
        assert "pagination" not in str(request.url)

    def test_list_returns_empty_list_when_no_results(self, httpx_mock: HTTPXMock):
        """List should return empty list when API returns no data."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json={"meta": {"status": 200}, "data": {"fields": []}},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.metadata_fields.list()

        assert result == []

    def test_list_raises_authentication_error_when_unauthorized(
        self, httpx_mock: HTTPXMock
    ):
        """List should raise AuthenticationError when API returns 401."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            status_code=401,
            json={"error": "Unauthorized"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import AuthenticationError

        with pytest.raises(AuthenticationError):
            client.v1.metadata_fields.list()


class TestMetadataFieldsGet:
    """Tests for MetadataFields.get() behavior."""

    def test_get_returns_single_field(self, httpx_mock: HTTPXMock):
        """Get should return a single metadata field by ID."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/42",
            json={
                "meta": {"status": 200},
                "data": {
                    "id": 42,
                    "name": "Diagnosis",
                    "contentType": "string",
                    "resourceType": "image",
                },
            },
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.metadata_fields.get(42)

        assert result["id"] == 42
        assert result["name"] == "Diagnosis"
        assert result["contentType"] == "string"

    def test_get_raises_not_found_for_invalid_id(self, httpx_mock: HTTPXMock):
        """Get should raise NotFoundError for non-existent field."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/99999",
            status_code=404,
            json={"error": "Metadata field not found"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import NotFoundError

        with pytest.raises(NotFoundError):
            client.v1.metadata_fields.get(99999)


class TestMetadataFieldsCreate:
    """Tests for MetadataFields.create() behavior."""

    def test_create_returns_created_field(self, httpx_mock: HTTPXMock):
        """Create should return the newly created metadata field."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            method="POST",
            json={
                "meta": {"status": 201},
                "data": {
                    "id": 100,
                    "name": "Patient ID",
                    "contentType": "string",
                    "resourceType": "image",
                },
            },
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.metadata_fields.create(
            {"name": "Patient ID", "contentType": "string", "resourceType": "image"}
        )

        assert result["id"] == 100
        assert result["name"] == "Patient ID"
        assert result["contentType"] == "string"

    def test_create_raises_validation_error_on_invalid_data(
        self, httpx_mock: HTTPXMock
    ):
        """Create should raise ValidationError when data is invalid."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            method="POST",
            status_code=400,
            json={"error": "Name is required"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import ValidationError

        with pytest.raises(ValidationError):
            client.v1.metadata_fields.create({})

    def test_create_raises_authentication_error_when_unauthorized(
        self, httpx_mock: HTTPXMock
    ):
        """Create should raise AuthenticationError when not authenticated."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            method="POST",
            status_code=401,
            json={"error": "Unauthorized"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import AuthenticationError

        with pytest.raises(AuthenticationError):
            client.v1.metadata_fields.create({"name": "Test"})


class TestMetadataFieldsUpdate:
    """Tests for MetadataFields.update() behavior."""

    def test_update_returns_updated_field(self, httpx_mock: HTTPXMock):
        """Update should use PATCH and return the updated metadata field."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/42",
            method="PATCH",
            json={
                "meta": {"status": 200},
                "data": {
                    "id": 42,
                    "name": "Updated Field Name",
                    "contentType": "number",
                },
            },
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.metadata_fields.update(
            42, {"name": "Updated Field Name", "contentType": "number"}
        )

        assert result["id"] == 42
        assert result["name"] == "Updated Field Name"
        assert result["contentType"] == "number"

    def test_update_raises_not_found_for_invalid_id(self, httpx_mock: HTTPXMock):
        """Update should raise NotFoundError for non-existent field."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/99999",
            method="PATCH",
            status_code=404,
            json={"error": "Metadata field not found"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import NotFoundError

        with pytest.raises(NotFoundError):
            client.v1.metadata_fields.update(99999, {"name": "Updated"})

    def test_update_raises_validation_error_on_invalid_data(
        self, httpx_mock: HTTPXMock
    ):
        """Update should raise ValidationError when data is invalid."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/42",
            method="PATCH",
            status_code=400,
            json={"error": "Invalid data"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import ValidationError

        with pytest.raises(ValidationError):
            client.v1.metadata_fields.update(42, {"invalid": "data"})

    def test_update_raises_authentication_error_when_unauthorized(
        self, httpx_mock: HTTPXMock
    ):
        """Update should raise AuthenticationError when not authenticated."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/42",
            method="PATCH",
            status_code=401,
            json={"error": "Unauthorized"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import AuthenticationError

        with pytest.raises(AuthenticationError):
            client.v1.metadata_fields.update(42, {"name": "Updated"})


class TestMetadataFieldsDelete:
    """Tests for MetadataFields.delete() behavior."""

    def test_delete_returns_successfully(self, httpx_mock: HTTPXMock):
        """Delete should complete successfully for valid ID."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/42",
            method="DELETE",
            status_code=204,
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.metadata_fields.delete(42)

        assert result is None

    def test_delete_raises_not_found_for_invalid_id(self, httpx_mock: HTTPXMock):
        """Delete should raise NotFoundError for non-existent field."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/99999",
            method="DELETE",
            status_code=404,
            json={"error": "Metadata field not found"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import NotFoundError

        with pytest.raises(NotFoundError):
            client.v1.metadata_fields.delete(99999)

    def test_delete_raises_authentication_error_when_unauthorized(
        self, httpx_mock: HTTPXMock
    ):
        """Delete should raise AuthenticationError when not authenticated."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/42",
            method="DELETE",
            status_code=401,
            json={"error": "Unauthorized"},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        from concentriq.exceptions import AuthenticationError

        with pytest.raises(AuthenticationError):
            client.v1.metadata_fields.delete(42)


SAMPLE_FIELDS_RESPONSE = {
    "meta": {"status": 200},
    "data": {
        "fields": [
            {
                "id": 1,
                "name": "Diagnosis",
                "contentType": "string",
                "resourceType": "image",
            },
            {
                "id": 2,
                "name": "Status",
                "contentType": "dropdown",
                "resourceType": "image",
                "dropdownOptions": [
                    {"id": 10, "value": "Active"},
                    {"id": 11, "value": "Inactive"},
                ],
            },
            {
                "id": 3,
                "name": "Created Date",
                "contentType": "date",
                "resourceType": "imageSet",
            },
            {
                "id": 4,
                "name": "Score",
                "contentType": "number",
                "resourceType": "image",
            },
        ]
    },
}


class TestMetadataFieldsGetByName:
    """Tests for MetadataFields.get_by_name() behavior."""

    def test_get_by_name_returns_matching_field(self, httpx_mock: HTTPXMock):
        """Should return the field definition matching the given name."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json=SAMPLE_FIELDS_RESPONSE,
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        field = client.v1.metadata_fields.get_by_name("Diagnosis")

        assert field["id"] == 1
        assert field["name"] == "Diagnosis"
        assert field["contentType"] == "string"

    def test_get_by_name_returns_none_when_not_found(self, httpx_mock: HTTPXMock):
        """Should return None when no field matches the given name."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json=SAMPLE_FIELDS_RESPONSE,
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.metadata_fields.get_by_name("Nonexistent Field")

        assert result is None

    def test_get_by_name_filters_by_resource_type(self, httpx_mock: HTTPXMock):
        """Should only match fields of the specified resource type."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json=SAMPLE_FIELDS_RESPONSE,
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        # "Created Date" is an imageSet field, not image
        result = client.v1.metadata_fields.get_by_name(
            "Created Date", resource_type="image"
        )
        assert result is None

        # But it should match with the correct resource type
        field = client.v1.metadata_fields.get_by_name(
            "Created Date", resource_type="imageSet"
        )
        assert field is not None
        assert field["id"] == 3

    def test_get_by_name_caches_fields_across_calls(self, httpx_mock: HTTPXMock):
        """Should only fetch fields from API once, then serve from cache."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json=SAMPLE_FIELDS_RESPONSE,
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        client.v1.metadata_fields.get_by_name("Diagnosis")
        client.v1.metadata_fields.get_by_name("Status")
        client.v1.metadata_fields.get_by_name("Score")

        # Only one HTTP request should have been made
        requests = httpx_mock.get_requests()
        assert len(requests) == 1

    def test_get_by_name_refresh_refetches_from_api(self, httpx_mock: HTTPXMock):
        """Should refetch from API when refresh=True."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json=SAMPLE_FIELDS_RESPONSE,
        )
        # Second response with updated data
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json={
                "meta": {"status": 200},
                "data": {
                    "fields": [
                        {
                            "id": 1,
                            "name": "Diagnosis",
                            "contentType": "string",
                            "resourceType": "image",
                        },
                        {
                            "id": 5,
                            "name": "New Field",
                            "contentType": "string",
                            "resourceType": "image",
                        },
                    ]
                },
            },
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        # First call - "New Field" doesn't exist yet
        result = client.v1.metadata_fields.get_by_name("New Field")
        assert result is None

        # After refresh - "New Field" should be found
        result = client.v1.metadata_fields.get_by_name("New Field", refresh=True)
        assert result is not None
        assert result["id"] == 5

        # Two HTTP requests total
        assert len(httpx_mock.get_requests()) == 2

    def test_get_by_name_cache_is_per_client_instance(self, httpx_mock: HTTPXMock):
        """Different client instances should have independent caches."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json=SAMPLE_FIELDS_RESPONSE,
        )
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/metadata-fields/",
            json=SAMPLE_FIELDS_RESPONSE,
        )

        client1 = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        client2 = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )

        client1.v1.metadata_fields.get_by_name("Diagnosis")
        client2.v1.metadata_fields.get_by_name("Diagnosis")

        # Each client should make its own request
        assert len(httpx_mock.get_requests()) == 2
