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

import pytest
import respx
from httpx import Response

from runblob import (
    RunBlob,
    VeoModel,
    VeoStatus,
    VeoGenerateResponse,
    VeoStatusResponse,
    AuthenticationError,
    NotFoundError,
    GenerationTimeoutError,
)


GEN_URL = "/v1/veo/generate"
STATUS_URL = "/v1/veo/generations/veo-123"
LIST_URL = "/v1/veo/generations"


class TestGenerate:
    async def test_basic_generate(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "generation_id": "veo-123",
                "status": "pending",
                "seed": 42,
                "price": "0.2500",
            })
        )

        result = await client.veo.generate(prompt="A cinematic sunset over the ocean waves")

        assert isinstance(result, VeoGenerateResponse)
        assert result.generation_id == "veo-123"
        assert result.status == VeoStatus.PENDING
        assert result.seed == 42
        assert result.price == "0.2500"

    async def test_generate_quality_4k(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "generation_id": "veo-4k",
                "status": "pending",
                "seed": 100,
                "price": "0.8000",
            })
        )

        result = await client.veo.generate(
            prompt="A timelapse of clouds moving over mountains at golden hour",
            model="veo_3_quality_4k",
            aspect_ratio="9:16",
            seed=100,
        )

        assert result.generation_id == "veo-4k"
        assert result.price == "0.8000"

    async def test_generate_with_enum(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "generation_id": "veo-enum",
                "status": "pending",
                "seed": 1,
                "price": "0.5000",
            })
        )

        result = await client.veo.generate(
            prompt="A dog running through a field of flowers in slow motion",
            model=VeoModel.QUALITY,
        )

        assert result.generation_id == "veo-enum"

    async def test_generate_with_watermark(self, client: RunBlob, mock_api):
        mock_api.post(GEN_URL).mock(
            return_value=Response(201, json={
                "generation_id": "veo-wm",
                "status": "pending",
                "seed": 5,
                "price": "0.2500",
            })
        )

        result = await client.veo.generate(
            prompt="A beautiful landscape with rolling hills and clear skies",
            watermark_text="RunBlob",
        )

        assert result.generation_id == "veo-wm"

    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.veo.generate(prompt="A test prompt that is long enough")


class TestGetStatus:
    async def test_completed(self, client: RunBlob, mock_api):
        mock_api.get(STATUS_URL).mock(
            return_value=Response(200, json={
                "generation_id": "veo-123",
                "status": "completed",
                "prompt": "A sunset",
                "video_url": "https://storage.runblob.io/veo/output.mp4",
                "seed": 42,
                "created_at": "2026-03-18T10:00:00Z",
                "completed_at": "2026-03-18T10:02:00Z",
            })
        )

        result = await client.veo.get_status("veo-123")

        assert result.is_completed
        assert result.video_url == "https://storage.runblob.io/veo/output.mp4"
        assert result.seed == 42

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

        result = await client.veo.get_status("veo-123")

        assert not result.is_terminal
        assert result.video_url is None

    async def test_failed(self, client: RunBlob, mock_api):
        mock_api.get(STATUS_URL).mock(
            return_value=Response(200, json={
                "generation_id": "veo-123",
                "status": "failed",
                "prompt": "A sunset",
                "error": {"message": "UNSAFE_CONTENT", "details": {"error_type": "moderation"}},
            })
        )

        result = await client.veo.get_status("veo-123")

        assert result.is_failed
        assert result.error is not None
        assert result.error["message"] == "UNSAFE_CONTENT"

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

        with pytest.raises(NotFoundError):
            await client.veo.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=[
                {"generation_id": "v1", "status": "completed", "prompt": "Cat", "video_url": "https://x.com/1.mp4"},
                {"generation_id": "v2", "status": "failed", "prompt": "Dog", "error": {"message": "TIMEOUT"}},
            ])
        )

        results = await client.veo.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={
                "generation_id": "veo-123",
                "status": "completed",
                "prompt": "test",
                "video_url": "https://storage.runblob.io/veo/out.mp4",
            })
        )

        result = await client.veo.wait_for_result("veo-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={"generation_id": "veo-123", "status": "processing", "prompt": "t"}),
            Response(200, json={"generation_id": "veo-123", "status": "processing", "prompt": "t"}),
            Response(200, json={"generation_id": "veo-123", "status": "completed", "prompt": "t", "video_url": "https://x.com/v.mp4"}),
        ]

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

        assert result.is_completed
        assert route.call_count == 3

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

        with pytest.raises(GenerationTimeoutError):
            await client.veo.wait_for_result("veo-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={
                "generation_id": "batch-1",
                "status": "pending",
                "seed": 1,
                "price": "0.2500",
            })
        )

        results = await client.veo.generate_batch(
            [
                "A cinematic sunset over the mountains with dramatic clouds",
                "A slow motion shot of waves crashing on rocky shores",
            ],
            model="veo_3_fast",
            concurrency=2,
        )

        assert len(results) == 2


class TestHasVeoResource:
    async def test_client_has_veo(self, client: RunBlob):
        assert hasattr(client, "veo")
