"""Tests for screenshot_batch.client module."""

from __future__ import annotations

from unittest.mock import MagicMock, Mock, patch
from uuid import UUID

import pytest

from screenshot_batch import ScreenshotServiceClient
from screenshot_batch.exceptions import (
    ScreenshotAPIError,
    ScreenshotBatchError,
    ScreenshotClientError,
    ScreenshotConfigError,
    ScreenshotTimeoutError,
)

# Valid UUID for testing
TEST_JOB_ID = "550e8400-e29b-41d4-a716-446655440000"


class TestScreenshotServiceClient:
    """Tests for ScreenshotServiceClient class."""

    def test_client_initialization(self):
        """Test basic client initialization."""
        client = ScreenshotServiceClient(
            base_url="https://test.example.com",
            timeout=30.0,
        )
        assert client.base_url == "https://test.example.com"
        assert client.timeout == 30.0

    def test_client_with_api_key(self):
        """Test client initialization with API key."""
        client = ScreenshotServiceClient(
            base_url="https://test.example.com",
            api_key="test-key",
        )
        # Client should be initialized with headers
        assert client.base_url == "https://test.example.com"

    def test_from_env_success(self):
        """Test creating client from environment variables."""
        with patch.dict(
            "os.environ",
            {
                "SCREENSHOT_FUNC_BASE_URL": "https://test.example.com",
                "SCREENSHOT_FUNC_API_KEY": "test-key",
            },
        ):
            client = ScreenshotServiceClient.from_env()
            assert client.base_url == "https://test.example.com"

    def test_from_env_missing_url(self):
        """Test from_env raises error when URL is missing."""
        with (
            patch.dict("os.environ", {}, clear=True),
            pytest.raises(ScreenshotConfigError, match="SCREENSHOT_FUNC_BASE_URL"),
        ):
            ScreenshotServiceClient.from_env()

    def test_from_env_custom_variable_names(self):
        """Test from_env with custom environment variable names."""
        with patch.dict(
            "os.environ",
            {
                "CUSTOM_URL": "https://custom.example.com",
                "CUSTOM_KEY": "custom-key",
            },
        ):
            client = ScreenshotServiceClient.from_env(
                url_env="CUSTOM_URL",
                api_key_env="CUSTOM_KEY",
            )
            assert client.base_url == "https://custom.example.com"


class TestExceptionHierarchy:
    """Tests for exception hierarchy."""

    def test_exception_inheritance(self):
        """Test that exceptions inherit correctly."""
        assert issubclass(ScreenshotAPIError, ScreenshotClientError)
        assert issubclass(ScreenshotBatchError, ScreenshotClientError)
        assert issubclass(ScreenshotConfigError, ScreenshotClientError)
        assert issubclass(ScreenshotTimeoutError, ScreenshotClientError)
        assert issubclass(ScreenshotClientError, Exception)

    def test_screenshot_api_error_attributes(self):
        """Test ScreenshotAPIError attributes."""
        error = ScreenshotAPIError(status_code=500, content=b"Internal Server Error", message="API failed")
        assert error.status_code == 500
        assert error.content == b"Internal Server Error"
        assert error.message == "API failed"
        assert "API failed" in str(error)

    def test_screenshot_batch_error_attributes(self):
        """Test ScreenshotBatchError attributes."""
        error = ScreenshotBatchError(
            batch_id="test-batch", job_id=TEST_JOB_ID, reason="Processing failed", details={"error_code": 123}
        )
        assert error.batch_id == "test-batch"
        assert error.job_id == TEST_JOB_ID
        assert error.reason == "Processing failed"
        assert error.details == {"error_code": 123}

    def test_screenshot_timeout_error_attributes(self):
        """Test ScreenshotTimeoutError attributes."""
        error = ScreenshotTimeoutError(timeout=600.0, last_response={"status": "running"})
        assert error.timeout == 600.0
        assert error.last_response == {"status": "running"}
        assert "600" in str(error)


class TestClientMethods:
    """Tests for client methods with mocked core client."""

    @pytest.fixture
    def mock_core_client(self):
        """Create a mock core client."""
        return MagicMock()

    @pytest.fixture
    def client(self, mock_core_client):
        """Create a client with mocked core client."""
        client = ScreenshotServiceClient(base_url="https://test.example.com")
        client._core_client = mock_core_client
        return client

    def test_start_batch_success(self, client, mock_core_client):
        """Test successful batch start."""
        from screenshot_client_core.models import ScreenshotRunHandle
        from screenshot_client_core.models.screenshot_run_handle_status import ScreenshotRunHandleStatus

        # Mock successful response
        mock_response = Mock()
        mock_response.status_code = 202
        # Create actual instance to pass isinstance check
        mock_response.parsed = ScreenshotRunHandle(
            batch_id="test-batch",
            job_id=UUID(TEST_JOB_ID),
            status=ScreenshotRunHandleStatus.QUEUED,
            status_url="https://example.com/status",
        )

        # Mock the API call
        with patch("screenshot_batch.client.start_screenshot_batch") as mock_start:
            mock_start.sync_detailed.return_value = mock_response

            handle = client.start_batch(
                batch_id="test-batch", payload={"jobs": [{"job_id": "1", "url": "https://example.com"}]}
            )

            assert handle.batch_id == "test-batch"
            assert str(handle.job_id) == TEST_JOB_ID

    def test_start_batch_api_error(self, client):
        """Test batch start with API error."""
        # Mock error response
        mock_response = Mock()
        mock_response.status_code = 400
        mock_response.content = b"Bad Request"

        with patch("screenshot_batch.client.start_screenshot_batch") as mock_start:
            mock_start.sync_detailed.return_value = mock_response

            with pytest.raises(ScreenshotAPIError) as exc_info:
                client.start_batch(batch_id="test-batch", payload={"jobs": []})

            assert exc_info.value.status_code == 400

    def test_get_run_success(self, client):
        """Test successful run status retrieval."""
        from screenshot_client_core.models import ScreenshotRunStatus
        from screenshot_client_core.models.screenshot_run_status_status import ScreenshotRunStatusStatus

        mock_response = Mock()
        mock_response.status_code = 200
        # Create actual instance to pass isinstance check
        mock_response.parsed = ScreenshotRunStatus(
            batch_id="test-batch",
            job_id=TEST_JOB_ID,
            status=ScreenshotRunStatusStatus.RUNNING,
            job_total=10,
            job_completed=5,
        )

        with patch("screenshot_batch.client.get_screenshot_run") as mock_get:
            mock_get.sync_detailed.return_value = mock_response

            status = client.get_run("test-batch", TEST_JOB_ID)

            assert status["status"] == "running"
            assert status["batch_id"] == "test-batch"

    def test_cancel_run_success(self, client):
        """Test successful run cancellation."""
        mock_response = Mock()
        mock_response.status_code = 200

        with patch("screenshot_batch.client.cancel_screenshot_run") as mock_cancel:
            mock_cancel.sync_detailed.return_value = mock_response

            # Should not raise
            client.cancel_run("test-batch", TEST_JOB_ID)

    def test_cancel_run_error(self, client):
        """Test run cancellation with error."""
        mock_response = Mock()
        mock_response.status_code = 404
        mock_response.content = b"Not Found"

        with patch("screenshot_batch.client.cancel_screenshot_run") as mock_cancel:
            mock_cancel.sync_detailed.return_value = mock_response

            with pytest.raises(ScreenshotAPIError) as exc_info:
                client.cancel_run("test-batch", TEST_JOB_ID)

            assert exc_info.value.status_code == 404


class TestPollingIntegration:
    """Tests for polling integration."""

    def test_poll_run_uses_status_poller(self):
        """Test that poll_run uses StatusPoller correctly."""
        client = ScreenshotServiceClient(base_url="https://test.example.com")

        # Mock get_run to return terminal status
        with patch.object(client, "get_run") as mock_get_run:
            mock_get_run.return_value = {
                "status": "completed",
                "batch_id": "test-batch",
                "job_id": TEST_JOB_ID,
                "job_total": 10,
                "job_completed": 10,
            }

            result = client.poll_run("test-batch", TEST_JOB_ID, timeout=10.0)

            assert result["status"] == "completed"
            assert mock_get_run.called


class TestImports:
    """Tests for package imports."""

    def test_public_api_imports(self):
        """Test that public API can be imported."""
        from screenshot_batch import (
            ScreenshotAPIError,
            ScreenshotClientError,
            ScreenshotServiceClient,
        )

        # All imports should be classes/exceptions
        assert callable(ScreenshotServiceClient)
        assert issubclass(ScreenshotClientError, Exception)
        assert issubclass(ScreenshotAPIError, Exception)

    def test_model_imports(self):
        """Test that models can be imported from core package."""
        from screenshot_client_core.models import (
            ScreenshotBatchRequest,
            ScreenshotRunHandle,
        )

        # All should be importable (from core package)
        assert ScreenshotBatchRequest is not None
        assert ScreenshotRunHandle is not None
