"""Tests for ChatGPT resource."""
from __future__ import annotations

import pytest
import respx
from httpx import Response

from runblob import (
    RunBlob,
    ChatGPTModel,
    ChatGPTStatus,
    ChatGPTGenerateResponse,
    ChatGPTStatusResponse,
    AuthenticationError,
    NotFoundError,
    GenerationTimeoutError,
)


GEN_URL = "/v1/chatgpt-images/generate"
STATUS_URL = "/v1/chatgpt-images/generations/task-123"
LIST_URL = "/v1/chatgpt-images/generations"


class TestGenerate:
    async def test_basic_generate(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "task_uuid": "task-123",
                "status": "pending",
                "price": "0.0390",
            })
        )

        result = await client.chatgpt.generate(prompt="A cute cat illustration")

        assert isinstance(result, ChatGPTGenerateResponse)
        assert result.task_uuid == "task-123"
        assert result.status == ChatGPTStatus.PENDING
        assert result.price == "0.0390"

    async def test_generate_with_aspect_ratio(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "task_uuid": "task-ar",
                "status": "pending",
                "price": "0.0390",
            })
        )

        result = await client.chatgpt.generate(
            prompt="A landscape painting of mountains",
            aspect_ratio="4:3",
        )

        assert result.task_uuid == "task-ar"

    async def test_generate_with_images(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "task_uuid": "task-img",
                "status": "pending",
                "price": "0.0390",
            })
        )

        result = await client.chatgpt.generate(
            prompt="Make this photo into a cartoon style",
            images=["https://example.com/photo.jpg"],
        )

        assert result.task_uuid == "task-img"

    async def test_generate_auth_error(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(401, json={"error": "Invalid API key"})
        )

        with pytest.raises(AuthenticationError):
            await client.chatgpt.generate(prompt="test")


class TestGetStatus:
    async def test_completed(self, client: RunBlob, mock_api):
        mock_api.get(STATUS_URL).mock(
            return_value=Response(200, json={
                "task_uuid": "task-123",
                "status": "completed",
                "prompt": "A cat",
                "result_image_url": "https://storage.runblob.io/chatgpt/out.png",
            })
        )

        result = await client.chatgpt.get_status("task-123")

        assert result.is_completed
        assert result.result_image_url == "https://storage.runblob.io/chatgpt/out.png"

    async def test_processing(self, client: RunBlob, mock_api):
        mock_api.get(STATUS_URL).mock(
            return_value=Response(200, json={
                "task_uuid": "task-123",
                "status": "processing",
                "prompt": "A cat",
            })
        )

        result = await client.chatgpt.get_status("task-123")

        assert not result.is_terminal

    async def test_failed(self, client: RunBlob, mock_api):
        mock_api.get(STATUS_URL).mock(
            return_value=Response(200, json={
                "task_uuid": "task-123",
                "status": "failed",
                "prompt": "A cat",
                "message": "OPENAI_DECLINED",
            })
        )

        result = await client.chatgpt.get_status("task-123")

        assert result.is_failed
        assert result.message == "OPENAI_DECLINED"

    async def test_not_found(self, client: RunBlob, mock_api):
        mock_api.get("/v1/chatgpt-images/generations/nonexistent").mock(
            return_value=Response(404, json={"error": "Not found"})
        )

        with pytest.raises(NotFoundError):
            await client.chatgpt.get_status("nonexistent")


class TestList:
    async def test_list_generations(self, client: RunBlob, mock_api):
        mock_api.get(LIST_URL).mock(
            return_value=Response(200, json=[
                {"task_uuid": "t1", "status": "completed", "prompt": "Cat", "result_image_url": "https://x.com/1.png"},
                {"task_uuid": "t2", "status": "failed", "prompt": "Dog", "message": "OPENAI_DECLINED"},
            ])
        )

        results = await client.chatgpt.list(limit=10)

        assert len(results) == 2
        assert results[0].is_completed
        assert results[1].is_failed


class TestWaitForResult:
    async def test_immediate_completion(self, client: RunBlob, mock_api):
        mock_api.get(STATUS_URL).mock(
            return_value=Response(200, json={
                "task_uuid": "task-123",
                "status": "completed",
                "prompt": "test",
                "result_image_url": "https://storage.runblob.io/out.png",
            })
        )

        result = await client.chatgpt.wait_for_result("task-123", timeout=5.0)
        assert result.is_completed

    async def test_polls_until_done(self, client: RunBlob, mock_api):
        route = mock_api.get(STATUS_URL)
        route.side_effect = [
            Response(200, json={"task_uuid": "task-123", "status": "processing", "prompt": "t"}),
            Response(200, json={"task_uuid": "task-123", "status": "completed", "prompt": "t", "result_image_url": "https://x.com/p.png"}),
        ]

        result = await client.chatgpt.wait_for_result(
            "task-123", timeout=30.0, poll_interval=0.01, max_poll_interval=0.02,
        )

        assert result.is_completed
        assert route.call_count == 2

    async def test_timeout(self, client: RunBlob, mock_api):
        mock_api.get(STATUS_URL).mock(
            return_value=Response(200, json={"task_uuid": "task-123", "status": "processing", "prompt": "t"})
        )

        with pytest.raises(GenerationTimeoutError):
            await client.chatgpt.wait_for_result("task-123", timeout=0.05, poll_interval=0.01)


class TestBatch:
    async def test_batch_generate(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "task_uuid": "batch-1",
                "status": "pending",
                "price": "0.0390",
            })
        )

        results = await client.chatgpt.generate_batch(
            ["A cat", "A dog", "A bird"],
            concurrency=3,
        )

        assert len(results) == 3


class TestHasChatGPTResource:
    async def test_client_has_chatgpt(self, client: RunBlob):
        assert hasattr(client, "chatgpt")
