"""Tests for the asynchronous AsyncPresentationsAI client."""

from __future__ import annotations

from typing import Any

import httpx
import pytest
import respx

from presentations_ai import (
    AsyncPresentationsAI,
    AuthenticationError,
    BadRequestError,
)
from presentations_ai.types import (
    AsyncJobResponse,
    ContentDocumentResponse,
    JobStatusResponse,
    PresentationResponse,
    SlideUpdateResponse,
)

TEST_API_KEY = "pai_test_key"
BASE = "https://developers.presentations.ai"


class TestAsyncClientConstructor:
    def test_requires_api_key(self) -> None:
        with pytest.raises(AuthenticationError):
            AsyncPresentationsAI(api_key="")

    @pytest.mark.asyncio
    async def test_context_manager(self) -> None:
        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            assert client._api_key == TEST_API_KEY


class TestAsyncCreateFromTopic:
    @pytest.mark.asyncio
    @respx.mock
    async def test_success(
        self,
        respx_mock: respx.MockRouter,
        presentation_response_data: dict[str, Any],
    ) -> None:
        respx_mock.post(f"{BASE}/api/v1/topic/document").mock(
            return_value=httpx.Response(200, json=presentation_response_data)
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.create_from_topic(
                topic="AI Strategy", export_type="pptx"
            )

        assert isinstance(result, PresentationResponse)
        assert result.docid == 12345

    @pytest.mark.asyncio
    @respx.mock
    async def test_returns_async_job(
        self,
        respx_mock: respx.MockRouter,
        async_job_response_data: dict[str, Any],
    ) -> None:
        respx_mock.post(f"{BASE}/api/v1/topic/document").mock(
            return_value=httpx.Response(200, json=async_job_response_data)
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.create_from_topic(
                topic="AI Strategy", export_type="pptx"
            )

        assert isinstance(result, AsyncJobResponse)
        assert result.job_id == "job_abc123"

    @pytest.mark.asyncio
    async def test_validates_empty_topic(self) -> None:
        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            with pytest.raises(BadRequestError):
                await client.create_from_topic(
                    topic="", export_type="pptx"
                )


class TestAsyncCreateFromFile:
    @pytest.mark.asyncio
    @respx.mock
    async def test_success(
        self,
        respx_mock: respx.MockRouter,
        presentation_response_data: dict[str, Any],
    ) -> None:
        respx_mock.post(f"{BASE}/api/v1/document/file").mock(
            return_value=httpx.Response(200, json=presentation_response_data)
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.create_from_file(
                file=b"PDF content",
                file_name="report.pdf",
                export_type="pptx",
            )

        assert isinstance(result, PresentationResponse)


class TestAsyncCreateSingleSlide:
    @pytest.mark.asyncio
    @respx.mock
    async def test_success(
        self,
        respx_mock: respx.MockRouter,
        presentation_response_data: dict[str, Any],
    ) -> None:
        respx_mock.post(f"{BASE}/api/v1/topic/singleslide").mock(
            return_value=httpx.Response(200, json=presentation_response_data)
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.create_single_slide(
                topic="Title", export_type="pptx"
            )

        assert isinstance(result, PresentationResponse)


class TestAsyncCreateFromContent:
    @pytest.mark.asyncio
    @respx.mock
    async def test_success(
        self,
        respx_mock: respx.MockRouter,
        content_document_response_data: dict[str, Any],
    ) -> None:
        respx_mock.post(f"{BASE}/api/v1/content/document").mock(
            return_value=httpx.Response(
                200, json=content_document_response_data
            )
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.create_from_content(
                name="Report",
                slides=[{"title": "S1", "content": "C1"}],
            )

        assert isinstance(result, ContentDocumentResponse)


class TestAsyncCreateFromRawContent:
    @pytest.mark.asyncio
    async def test_validates_empty_content(self) -> None:
        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            with pytest.raises(BadRequestError):
                await client.create_from_raw_content(
                    content="   ", export_type="pptx"
                )

    @pytest.mark.asyncio
    @respx.mock
    async def test_success(
        self,
        respx_mock: respx.MockRouter,
        presentation_response_data: dict[str, Any],
    ) -> None:
        respx_mock.post(f"{BASE}/api/v1/document/content").mock(
            return_value=httpx.Response(200, json=presentation_response_data)
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.create_from_raw_content(
                content="Meeting notes...", export_type="pptx"
            )

        assert isinstance(result, PresentationResponse)


class TestAsyncUpdateSlides:
    @pytest.mark.asyncio
    @respx.mock
    async def test_success(self, respx_mock: respx.MockRouter) -> None:
        respx_mock.post(f"{BASE}/api/v1/document/slide").mock(
            return_value=httpx.Response(
                200, json={"status": 0, "message": "Updated"}
            )
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.update_slides(
                doc_id=1,
                slides=[
                    {
                        "action": "update",
                        "slide_content": "New",
                        "index": 0,
                    }
                ],
            )

        assert isinstance(result, SlideUpdateResponse)


class TestAsyncCheckJobStatus:
    @pytest.mark.asyncio
    @respx.mock
    async def test_success(
        self,
        respx_mock: respx.MockRouter,
        job_status_complete_data: dict[str, Any],
    ) -> None:
        respx_mock.get(f"{BASE}/api/v1/polljob/job_123").mock(
            return_value=httpx.Response(200, json=job_status_complete_data)
        )

        async with AsyncPresentationsAI(api_key=TEST_API_KEY) as client:
            result = await client.check_job_status("job_123")

        assert isinstance(result, JobStatusResponse)
        assert result.status == 0


class TestAsyncHttpErrorMapping:
    @pytest.mark.asyncio
    @respx.mock
    async def test_401_raises_authentication_error(
        self, respx_mock: respx.MockRouter
    ) -> None:
        respx_mock.post(f"{BASE}/api/v1/topic/document").mock(
            return_value=httpx.Response(
                401, json={"error": "Invalid key"}
            )
        )

        async with AsyncPresentationsAI(
            api_key=TEST_API_KEY, max_retries=0
        ) as client:
            with pytest.raises(AuthenticationError):
                await client.create_from_topic(
                    topic="test", export_type="pptx"
                )
