import httpx
from src.application.ports.llm_service_port import LLMServicePort
from src.application.dtos.LLM import LLMRequestDTO, LLMResponseDTO
from typing import Optional
import base64
import io
from PIL import Image
import json
import tempfile
import os

class ChartGemmaService(LLMServicePort):
    def __init__(self, gradio_url: str):
        self.gradio_url = gradio_url
    
    async def analyze(self, request: LLMRequestDTO) -> LLMResponseDTO:
        try:
            if not request.image_bytes:
                return LLMResponseDTO(
                    answer="Error: No image data provided",
                    model_used="ChartGemma",
                    processing_time=None
                )

            # Create temporary file
            with tempfile.NamedTemporaryFile(suffix='.png', delete=True) as tmp_file:
                try:
                    tmp_file.write(request.image_bytes)
                    tmp_file.flush()
                    #os.chmod(tmp_file.name, 0o644)
                    # Explicitly close the file handle before API call
                    tmp_file.close()

                    # Call Gradio API - NOTE: Changed parameter name to match method
                    response = await self._call_gradio_api(
                        image_path =tmp_file.name,  # This matches the method parameter
                        question =request.question
                    )

                    return LLMResponseDTO(
                        answer=response,
                        model_used="ChartGemma",
                        processing_time=None
                    )

                finally:
                    try:
                        os.unlink(tmp_file.name)
                    except:
                        pass
                    
        except Exception as e:
            return LLMResponseDTO(
                answer=f"Error analyzing chart: {str(e)}",
                model_used="ChartGemma",
                processing_time=None
            )

    async def _call_gradio_api(self, image_path: str, question: str) -> str:
        """Robust Gradio API caller with proper error handling and resource management"""
        payload = {
            "data": [
                image_path,  # File path
                question
            ]
        }

        # Configure HTTP transport with retries
        transport = httpx.AsyncHTTPTransport(
            retries=3,  # Automatically retry failed requests
            verify=False  # Only use if you get SSL errors (not recommended for production)
        )

        async with httpx.AsyncClient(
            transport=transport,
            timeout=30.0,
            follow_redirects=True  # Important for Gradio's HTTPS redirects
        ) as client:
            # Try both common Gradio endpoint patterns
            endpoints = [
                "https://c55d0e06f974d4ba67.gradio.live/run/predict/",  # Older versions
                "https://c55d0e06f974d4ba67.gradio.live/"   # Newer versions
            ]

            for endpoint in endpoints:
                try:
                    response = await client.post(
                        endpoint,
                        json=payload,
                        headers={"Content-Type": "application/json"}
                    )
                    response.raise_for_status()  # Raises HTTPStatusError for 4XX/5XX

                    # Debug output
                    print(f"Successful API call to {endpoint}")
                    print(f"Response: {response.text[:200]}...")  # Truncate long responses

                    return response.json()["data"][0]

                except httpx.HTTPStatusError as e:
                    error_detail = f"Status: {e.response.status_code}"
                    if e.response.content:
                        error_detail += f" | Response: {e.response.text[:200]}"
                    print(f"API Error at {endpoint}: {error_detail}")
                    continue  # Try next endpoint

                except httpx.RequestError as e:
                    print(f"Network error at {endpoint}: {str(e)}")
                    continue  # Try next endpoint

            # All endpoints failed
            return "Error: Failed to connect to Gradio API after multiple attempts"


    def _load_image(self, image_data: Optional[bytes]) -> Optional[Image.Image]:
        if not image_data:
            return None
        try:
            return Image.open(io.BytesIO(image_data)).convert("RGB")
        except Exception as e:
            print(f"Error loading image: {e}")
            return None
    
    