"""Integration tests for OpenAI image generator with real API.

These tests require valid API credentials and make real API calls.
Run with: pytest -m integration
Skip with: pytest -m "not integration"
"""

import base64
import logging
import os
import tempfile
from pathlib import Path

import pytest
from dotenv import load_dotenv

from appkit_mcp_image.backend.generators.openai import OpenAIImageGenerator
from appkit_mcp_image.backend.models import (
    EditImageInput,
    GenerationInput,
)

logger = logging.getLogger(__name__)

# Load environment variables from .env file
load_dotenv()


# Skip all tests in this module if no API key is available
pytestmark = pytest.mark.skipif(
    not os.environ.get("OPENAI_API_KEY"),
    reason="OPENAI_API_KEY not set - skipping integration tests",
)


def get_image_path(image_url_or_base64: str, test_name: str) -> str:
    """Get image file path from generator response.

    The generator returns URLs pointing to saved files on disk.
    This helper extracts the local file path or logs the URL info.

    Args:
        image_url_or_base64: Either a URL (http://...) or base64 string
        test_name: Test name for logging

    Returns:
        Path to the image file on disk
    """
    # Get temp directory from environment or use system temp
    tmp_dir_path = os.environ.get("TMP_PATH")
    tmp_dir = Path(tmp_dir_path) if tmp_dir_path else Path(tempfile.gettempdir())

    # Check if it's a URL (the generator saves files and returns URLs)
    if image_url_or_base64.startswith(("http://", "https://")):
        # URL format: http://localhost:8000/_upload/gpt-image-xxxxx.png
        # Extract filename and construct local path
        filename = image_url_or_base64.rsplit("/", maxsplit=1)[-1]
        filepath = tmp_dir / filename

        if filepath.exists():
            logger.info(
                "Image generated by server and saved at: %s (URL: %s)",
                filepath,
                image_url_or_base64,
            )
            return str(filepath)
        logger.warning(
            "Image URL provided but file not found at %s. "
            "Image may not have been saved locally by the generator. "
            "URL: %s",
            filepath,
            image_url_or_base64,
        )
        return str(filepath)

    # Otherwise, it's base64 data (fallback for direct base64 responses)
    logger.debug("Image data is base64 encoded, decoding and saving")
    tmp_dir.mkdir(parents=True, exist_ok=True)

    # Create unique filename
    filename = f"openai_generated_{test_name}.png"
    filepath = tmp_dir / filename

    # Decode and save
    try:
        image_bytes = base64.b64decode(image_url_or_base64)
        filepath.write_bytes(image_bytes)
        logger.info("Decoded and saved base64 image to: %s", filepath)
        return str(filepath)
    except Exception as e:
        logger.error("Failed to decode base64 image: %s", e)
        raise


@pytest.mark.integration
class TestOpenAIIntegrationGenerate:
    """Integration tests for OpenAI image generation."""

    @pytest.mark.asyncio
    async def test_generate_basic_image(self, openai_image_generator) -> None:
        """Test basic image generation with real API."""
        input_data = GenerationInput(prompt="A red square on white background")

        response = await openai_image_generator.generate(input_data)

        assert response.state == "succeeded"
        assert len(response.images) == 1
        assert isinstance(response.images[0], str)
        assert len(response.images[0]) > 0  # Base64 string should have content

        # Get image path (generator already saved it)
        image_path = get_image_path(response.images[0], "test_generate_basic_image")
        logger.info("Generated image saved to: %s", image_path)


@pytest.mark.integration
class TestOpenAIIntegrationEdit:
    """Integration tests for OpenAI image editing."""

    @pytest.mark.asyncio
    async def test_edit_basic(
        self,
        openai_image_generator,
        temp_image_file: str,
    ) -> None:
        """Test basic image editing with real API."""
        input_data = EditImageInput(
            prompt="Make the image blue",
            image_paths=[temp_image_file],
        )

        response = await openai_image_generator.edit(input_data)

        assert response.state == "succeeded"
        assert len(response.images) == 1
        assert isinstance(response.images[0], str)
        assert len(response.images[0]) > 0

        # Get image path (generator already saved it)
        image_path = get_image_path(response.images[0], "test_edit_basic")
        logger.info("Edited image saved to: %s", image_path)

    @pytest.mark.asyncio
    async def test_edit_with_different_format(
        self,
        openai_image_generator,
        temp_image_file: str,
    ) -> None:
        """Test editing with different output format."""
        input_data = EditImageInput(
            prompt="Convert to grayscale",
            image_paths=[temp_image_file],
            output_format="jpeg",
        )

        response = await openai_image_generator.edit(input_data)

        assert response.state == "succeeded"
        assert len(response.images) == 1
        assert isinstance(response.images[0], str)

        # Get image path (generator already saved it)
        image_path = get_image_path(
            response.images[0], "test_edit_with_different_format"
        )
        logger.info("Edited image (jpeg format) saved to: %s", image_path)


@pytest.mark.integration
class TestOpenAIIntegrationMarkdownOutput:
    """Integration tests demonstrating raw markdown response format."""

    @pytest.mark.asyncio
    async def test_generate_image_markdown_response(
        self, openai_image_generator
    ) -> None:
        """Demonstrate the raw markdown response from image generation.

        This test shows how the response looks in markdown format,
        useful for documentation and understanding the API output structure.
        """
        input_data = GenerationInput(
            prompt=(
                "A beautiful sunset over mountains with vibrant "
                "orange and purple colors"
            )
        )

        response = await openai_image_generator.generate(input_data)

        # Build markdown output showing the response structure
        markdown_output = f"""# Image Generation Response

## Response State
- **State**: `{response.state}`
- **Error**: `{response.error if response.error else "None"}`

## Generated Images
- **Count**: {len(response.images)}
- **Images**:
"""
        for idx, image_url in enumerate(response.images, 1):
            markdown_output += f"\n  {idx}. URL: `{image_url}`"
            # Extract filename from URL
            if "/" in image_url:
                filename = image_url.split("/")[-1]
                markdown_output += f"\n     Filename: `{filename}`"

        markdown_output += "\n\n## Response Details\n"
        markdown_output += "- Response type: `ImageGeneratorResponse`\n"
        img_count = len(response.images)
        markdown_output += f"- Images field type: `list[str]` ({img_count} items)\n"

        # Log the markdown output
        logger.info("Raw markdown response:\n%s", markdown_output)

        # Standard assertions
        assert response.state == "succeeded"
        assert len(response.images) > 0
        assert isinstance(response.images[0], str)


@pytest.mark.integration
class TestOpenAIIntegrationErrorHandling:
    """Integration tests for error handling."""

    @pytest.mark.asyncio
    async def test_invalid_api_key(self, openai_base_url: str) -> None:
        """Test handling of invalid API key."""
        generator = OpenAIImageGenerator(
            api_key="invalid_key_12345",
            backend_server="http://localhost:8000",
            base_url=openai_base_url,
        )

        input_data = GenerationInput(prompt="Test")

        response = await generator.generate(input_data)

        # Invalid API key should result in failed response
        assert response.state == "failed"
        assert len(response.error) > 0

    @pytest.mark.asyncio
    async def test_empty_prompt(self) -> None:
        """Test that empty prompt is allowed (validation doesn't require it)."""
        # Empty prompt is technically allowed by Pydantic
        # But the API will reject it
        input_data = GenerationInput(prompt="")
        # This should work at the model level
        assert input_data.prompt == ""
