"""
candy.imagegen — Génération d'images via l'API Candy v19
=========================================================

Permet de demander des images au tunnel via l'API Cedric7-Thinking.
Le serveur génère les images via un modèle compatible (Stable Diffusion, DALL-E, etc.)

Usage :
    from candy import ImageGen

    # Génère une image et la sauvegarde
    ImageGen.create("un chat astronaute sur la lune", save="chat_lune.png")

    # Retourne les données base64
    b64 = ImageGen.create("paysage montagneux au coucher du soleil")

    # Avec paramètres avancés
    ImageGen.create(
        prompt="portrait d'un robot steampunk",
        style="photorealistic",
        size="1024x1024",
        save="robot.png"
    )

    # Variation d'une image existante
    ImageGen.variation("mon_image.png", strength=0.7, save="variation.png")

    # Génération en batch
    images = ImageGen.batch([
        "forêt enchantée au lever du soleil",
        "ville futuriste la nuit",
        "portrait impressionniste"
    ])
"""

import base64
import json
import time
from pathlib import Path
from typing import Optional, Union
import httpx

# URL candy.json pour trouver le tunnel
CANDY_JSON_URL = "https://client.hubworld.net/candy.json"

_url_cache: Optional[str] = None
_url_cache_time: float = 0
URL_CACHE_TTL = 300  # 5 minutes


def _get_api_base() -> str:
    global _url_cache, _url_cache_time
    now = time.time()
    if _url_cache and (now - _url_cache_time) < URL_CACHE_TTL:
        return _url_cache
    try:
        r = httpx.get(CANDY_JSON_URL, timeout=10)
        r.raise_for_status()
        d = r.json()
        if d.get("is_closed", True):
            raise RuntimeError("API Candy hors ligne.")
        link = d.get("link", "").strip().rstrip("/")
        if not link:
            raise RuntimeError("Aucun lien API dans candy.json.")
        _url_cache      = link
        _url_cache_time = now
        return link
    except Exception as e:
        raise RuntimeError(f"Impossible de joindre l'API Candy : {e}")


class ImageGenError(Exception):
    pass


class _ImageGen:
    """
    Client de génération d'images via l'API Candy v19.
    Utilise le même tunnel Cloudflare que le reste de candy.
    """

    def create(
        self,
        prompt: str,
        *,
        style:   str = "default",
        size:    str = "512x512",
        steps:   int = 20,
        guidance: float = 7.5,
        negative: str = "",
        save:    Optional[str] = None,
        timeout: int = 120,
    ) -> Union[str, bytes]:
        """
        Génère une image à partir d'un prompt texte.

        Args:
            prompt:   Description de l'image à générer
            style:    Style visuel — "default" "photorealistic" "anime" "painting"
                      "sketch" "3d" "pixel" "watercolor" "oil" "comic"
            size:     Dimensions — "256x256" "512x512" "768x768" "1024x1024"
            steps:    Nombre d'étapes de débruitage (10-50)
            guidance: Guidance scale (1-20, défaut 7.5)
            negative: Prompt négatif (ce qu'on ne veut pas)
            save:     Chemin de sauvegarde (ex: "image.png"). Si None, retourne le base64.
            timeout:  Timeout en secondes

        Returns:
            str (base64) si save=None, sinon None (fichier sauvegardé)

        Exemple::
            # Sauvegarde directe
            ImageGen.create("chat astronaute", save="result.png")

            # Récupère le base64
            b64 = ImageGen.create("paysage montagneux", size="1024x1024")
        """
        base = _get_api_base()
        payload = {
            "prompt":        prompt,
            "style":         style,
            "size":          size,
            "steps":         steps,
            "guidance_scale": guidance,
            "negative_prompt": negative,
        }

        try:
            r = httpx.post(
                f"{base}/generate/image",
                json=payload,
                timeout=timeout,
            )
            r.raise_for_status()
            data = r.json()
        except httpx.HTTPStatusError as e:
            raise ImageGenError(f"Erreur API ({e.response.status_code}): {e.response.text}")
        except Exception as e:
            raise ImageGenError(f"Erreur réseau : {e}")

        if "error" in data:
            raise ImageGenError(data["error"])

        b64 = data.get("image_b64") or data.get("image") or ""
        if not b64:
            raise ImageGenError("Aucune image reçue de l'API.")

        if save:
            path = Path(save)
            path.parent.mkdir(parents=True, exist_ok=True)
            image_bytes = base64.b64decode(b64)
            path.write_bytes(image_bytes)
            return str(path)

        return b64

    def variation(
        self,
        image_path: str,
        *,
        prompt: str = "",
        strength: float = 0.7,
        save: Optional[str] = None,
        timeout: int = 120,
    ) -> Union[str, bytes]:
        """
        Génère une variation d'une image existante.

        Args:
            image_path: Chemin vers l'image source
            prompt:     Description optionnelle pour guider la variation
            strength:   Force de la variation (0.0=identique, 1.0=totalement différent)
            save:       Chemin de sauvegarde
            timeout:    Timeout en secondes

        Exemple::
            ImageGen.variation("mon_image.png", strength=0.5, save="variation.png")
        """
        path = Path(image_path)
        if not path.exists():
            raise ImageGenError(f"Image introuvable : {image_path}")

        image_bytes = path.read_bytes()
        b64_input   = base64.b64encode(image_bytes).decode("utf-8")
        base_url    = _get_api_base()

        payload = {
            "image_b64": b64_input,
            "prompt":    prompt,
            "strength":  strength,
        }

        try:
            r = httpx.post(
                f"{base_url}/generate/image/variation",
                json=payload,
                timeout=timeout,
            )
            r.raise_for_status()
            data = r.json()
        except Exception as e:
            raise ImageGenError(f"Erreur réseau : {e}")

        b64 = data.get("image_b64") or data.get("image") or ""
        if not b64:
            raise ImageGenError("Aucune variation reçue.")

        if save:
            p = Path(save)
            p.write_bytes(base64.b64decode(b64))
            return str(p)
        return b64

    def batch(
        self,
        prompts: list,
        *,
        style: str = "default",
        size:  str = "512x512",
        save_dir: Optional[str] = None,
        timeout: int = 120,
    ) -> list:
        """
        Génère plusieurs images en une fois.

        Args:
            prompts:  Liste de prompts texte
            style:    Style appliqué à toutes les images
            size:     Taille appliquée à toutes les images
            save_dir: Dossier de sauvegarde (optionnel)
            timeout:  Timeout par image

        Returns:
            Liste de base64 (ou chemins si save_dir spécifié)

        Exemple::
            images = ImageGen.batch([
                "forêt enchantée",
                "ville futuriste",
                "portrait robot",
            ], save_dir="./generated/")
        """
        results = []
        for i, prompt in enumerate(prompts):
            save = None
            if save_dir:
                save = str(Path(save_dir) / f"image_{i+1:03d}.png")
            result = self.create(
                prompt, style=style, size=size, save=save, timeout=timeout
            )
            results.append(result)
        return results

    def describe(self, image_path: str, lang: str = "FR") -> str:
        """
        Envoie une image à l'API et demande une description textuelle.

        Args:
            image_path: Chemin vers l'image
            lang:       Langue de la description

        Returns:
            Description textuelle de l'image

        Exemple::
            desc = ImageGen.describe("photo.jpg", lang="FR")
            print(desc)
        """
        path = Path(image_path)
        if not path.exists():
            raise ImageGenError(f"Image introuvable : {image_path}")

        b64 = base64.b64encode(path.read_bytes()).decode("utf-8")
        suffix = path.suffix.lower().lstrip(".")
        mime_map = {"jpg": "jpeg", "jpeg": "jpeg", "png": "png",
                    "webp": "webp", "gif": "gif"}
        mime = f"image/{mime_map.get(suffix, 'png')}"

        base_url = _get_api_base()
        payload  = {
            "image_b64":  b64,
            "mime_type":  mime,
            "lang":       lang,
        }

        try:
            r = httpx.post(f"{base_url}/generate/image/describe",
                           json=payload, timeout=60)
            r.raise_for_status()
            return r.json().get("description", "")
        except Exception as e:
            raise ImageGenError(f"Erreur describe : {e}")

    def styles(self) -> list:
        """Retourne les styles disponibles."""
        return [
            "default", "photorealistic", "anime", "painting",
            "sketch", "3d", "pixel", "watercolor", "oil",
            "comic", "cyberpunk", "fantasy", "minimalist",
            "portrait", "landscape", "abstract",
        ]

    def sizes(self) -> list:
        """Retourne les tailles disponibles."""
        return ["256x256", "512x512", "768x768", "1024x1024", "1024x768", "768x1024"]


ImageGen = _ImageGen()
