"""Tests for ImageSets client."""

import json

import pytest
from pytest_httpx import HTTPXMock

from concentriq import ConcentriqClient
from concentriq.auth import ApiKeyAuth
from concentriq.models.v1.filters import ImageSetFilters


class TestImageSetsList:
    """Tests for ImageSets.list() behavior."""

    def test_list_returns_list_of_image_sets(self, httpx_mock: HTTPXMock):
        """List should return a list of image set data from nested response."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets",
            json={
                "meta": {"status": 200, "duration": 123},
                "data": {
                    "imageSets": [
                        {"id": 1, "name": "Test Set 1"},
                        {"id": 2, "name": "Test Set 2"},
                    ]
                },
            },
        )

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

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

    def test_list_sends_pagination_as_json_string(self, httpx_mock: HTTPXMock):
        """List should send pagination as JSON string in query param."""
        httpx_mock.add_response(
            json={"meta": {"status": 200}, "data": {"imageSets": []}},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        client.v1.image_sets.list(page=2, rows_per_page=50)

        request = httpx_mock.get_request()
        pagination_param = request.url.params.get("pagination")
        assert pagination_param is not None
        parsed = json.loads(pagination_param)
        assert parsed["page"] == 2
        assert parsed["rowsPerPage"] == 50

    def test_list_sends_sorting_in_pagination_json(self, httpx_mock: HTTPXMock):
        """List should include sort params in pagination JSON."""
        httpx_mock.add_response(
            json={"meta": {"status": 200}, "data": {"imageSets": []}},
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        client.v1.image_sets.list(sort_by="name", descending=True)

        request = httpx_mock.get_request()
        pagination_param = request.url.params.get("pagination")
        parsed = json.loads(pagination_param)
        assert parsed["sortBy"] == "name"
        assert parsed["descending"] is True

    def test_list_with_filters(self, httpx_mock: HTTPXMock):
        """List should send filter parameters to API."""
        filters = ImageSetFilters(imageSetId=[1, 2, 3], name=["Test"])

        httpx_mock.add_response(
            json={"meta": {"status": 200}, "data": {"imageSets": []}},
        )

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

        assert isinstance(result, list)

    def test_list_with_created_date_filter_as_list(self, httpx_mock: HTTPXMock):
        """Created filter should accept list of date range dicts."""
        filters = ImageSetFilters(
            created=[{"start": "2024-01-01", "end": "2024-12-31"}]
        )

        httpx_mock.add_response(
            json={"meta": {"status": 200}, "data": {"imageSets": []}},
        )

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

        request = httpx_mock.get_request()
        filters_param = request.url.params.get("filters")
        parsed = json.loads(filters_param)
        assert isinstance(parsed["created"], list)
        assert parsed["created"][0]["start"] == "2024-01-01"

    def test_list_with_include_metadata(self, httpx_mock: HTTPXMock):
        """List should send includeMetadata param when requested."""
        httpx_mock.add_response(
            json={"meta": {"status": 200}, "data": {"imageSets": []}},
        )

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

        request = httpx_mock.get_request()
        assert request.url.params.get("includeMetadata") == "true"

    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/imageSets",
            json={"meta": {"status": 200}, "data": {"imageSets": []}},
        )

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

        assert result == []

    def test_list_returns_paginated_response_with_meta(self, httpx_mock: HTTPXMock):
        """List should return PaginatedResponse with accessible meta."""
        httpx_mock.add_response(
            json={
                "meta": {
                    "status": 200,
                    "pagination": {
                        "page": 1,
                        "rowsPerPage": 20,
                        "total": 100,
                    },
                },
                "data": {
                    "imageSets": [
                        {"id": 1, "name": "Test Set 1"},
                    ]
                },
            },
        )

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

        # Still works as a list
        assert len(result) == 1
        assert result[0]["id"] == 1

        # But also has meta
        assert result.meta["pagination"]["total"] == 100

    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/imageSets",
            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.image_sets.list()

    def test_list_raises_not_found_error_when_404(self, httpx_mock: HTTPXMock):
        """List should raise NotFoundError when API returns 404."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets",
            status_code=404,
            json={"error": "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.image_sets.list()


class TestImageSetsGet:
    """Tests for ImageSets.get() behavior."""

    def test_get_returns_single_image_set(self, httpx_mock: HTTPXMock):
        """Get should return a single image set by ID."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets/123",
            json={
                "meta": {"status": 200},
                "data": {"id": 123, "name": "Test Set", "imageCount": 5},
            },
        )

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

        assert result["id"] == 123
        assert result["name"] == "Test Set"
        assert result["imageCount"] == 5

    def test_get_raises_not_found_for_invalid_id(self, httpx_mock: HTTPXMock):
        """Get should raise NotFoundError for non-existent image set."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets/99999",
            status_code=404,
            json={"error": "ImageSet 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.image_sets.get(99999)


class TestImageSetsCreate:
    """Tests for ImageSets.create() behavior."""

    def test_create_returns_created_image_set(self, httpx_mock: HTTPXMock):
        """Create should return the newly created image set data."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets",
            method="POST",
            json={
                "meta": {"status": 201},
                "data": {
                    "id": 456,
                    "name": "New Image Set",
                    "description": "Test description",
                },
            },
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.image_sets.create(
            {"name": "New Image Set", "description": "Test description"}
        )

        assert result["id"] == 456
        assert result["name"] == "New Image Set"
        assert result["description"] == "Test description"

    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/imageSets",
            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.image_sets.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/imageSets",
            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.image_sets.create({"name": "Test"})


class TestImageSetsUpdate:
    """Tests for ImageSets.update() behavior."""

    def test_update_returns_updated_image_set(self, httpx_mock: HTTPXMock):
        """Update should use PATCH and return the updated image set data."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets/123",
            method="PATCH",
            json={
                "meta": {"status": 200},
                "data": {
                    "id": 123,
                    "name": "Updated Name",
                    "description": "Updated description",
                },
            },
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        result = client.v1.image_sets.update(
            123, {"name": "Updated Name", "description": "Updated description"}
        )

        assert result["id"] == 123
        assert result["name"] == "Updated Name"
        assert result["description"] == "Updated description"

    def test_update_raises_not_found_for_invalid_id(self, httpx_mock: HTTPXMock):
        """Update should raise NotFoundError for non-existent image set."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets/99999",
            method="PATCH",
            status_code=404,
            json={"error": "ImageSet 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.image_sets.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/imageSets/123",
            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.image_sets.update(123, {"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/imageSets/123",
            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.image_sets.update(123, {"name": "Updated"})


class TestImageSetsDelete:
    """Tests for ImageSets.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/imageSets/123",
            method="DELETE",
            status_code=204,
        )

        client = ConcentriqClient(
            base_url="https://test.concentriq.com", auth=ApiKeyAuth("test-key")
        )
        # Delete typically returns None or empty response
        result = client.v1.image_sets.delete(123)

        assert result is None

    def test_delete_raises_not_found_for_invalid_id(self, httpx_mock: HTTPXMock):
        """Delete should raise NotFoundError for non-existent image set."""
        httpx_mock.add_response(
            url="https://test.concentriq.com/api/imageSets/99999",
            method="DELETE",
            status_code=404,
            json={"error": "ImageSet 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.image_sets.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/imageSets/123",
            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.image_sets.delete(123)
