#!/usr/bin/env python3
"""Démonstrateur REST Higgsfield. Soumission unique, puis reprise par request_id.

Dépendance : httpx. Aucun appel n'est effectué à l'import.
Contrôlé localement avec des réponses simulées, sans génération payante.
"""
import argparse
import json
import os
from pathlib import Path
import random
import time
from urllib.parse import urlsplit

import httpx

API = "https://api.higgsfield.ai"
MODEL = "alibaba/wan-3.0/text-to-video"
TERMINAL = {"completed", "failed", "nsfw", "canceled"}


def save(path, value):
    temporary = path.with_suffix(path.suffix + ".tmp")
    temporary.write_text(json.dumps(value, ensure_ascii=False, indent=2), encoding="utf-8")
    temporary.replace(path)


def run(directory, submit=False, minutes=15):
    directory = Path(directory)
    directory.mkdir(parents=True, exist_ok=True)
    headers = {"Authorization": f"Key {os.environ['HF_API_KEY_ID']}:{os.environ['HF_API_KEY_SECRET']}"}
    state_path = directory / "request.json"
    with httpx.Client(timeout=30) as client:
        if submit:
            # Le marqueur reste présent après un timeout : ne jamais resoumettre à l'aveugle.
            with (directory / "submission.lock").open("x", encoding="utf-8") as marker:
                marker.write("Soumission tentée. Vérifier la Console si request.json manque.\n")
            payload = {
                "prompt": "Slow camera move over a miniature paper city, soft daylight, no text, no logos",
                "resolution": "720p",
                "aspect_ratio": "16:9",
                "duration": 5,
                "generate_audio": False,
            }
            save(directory / "input.json", {"model": MODEL, "arguments": payload})
            # Pas de retry automatique pour ce POST facturable.
            response = client.post(f"{API}/{MODEL}", headers=headers, json=payload)
            response.raise_for_status()
            accepted = response.json()
            save(state_path, accepted)
            print("Requête enregistrée :", accepted["request_id"])
        state = json.loads(state_path.read_text(encoding="utf-8"))
        status_url = state["status_url"]
        parsed = urlsplit(status_url)
        if parsed.scheme != "https" or parsed.netloc != "api.higgsfield.ai":
            raise ValueError("Hôte de suivi inattendu : aucun identifiant ne sera transmis.")
        deadline, delay = time.monotonic() + minutes * 60, 2.0
        result = None
        while time.monotonic() < deadline:
            try:
                response = client.get(status_url, headers=headers)
                if response.status_code >= 500:
                    time.sleep(delay + random.random())
                    delay = min(delay * 1.5, 10)
                    continue
                response.raise_for_status()
                result = response.json()
            except httpx.TransportError:
                time.sleep(delay + random.random())
                delay = min(delay * 1.5, 10)
                continue
            status = result.get("status")
            if status in TERMINAL:
                save(directory / "result.json", result)
                if status != "completed":
                    raise RuntimeError(f"Génération terminée avec le statut {status} ; voir result.json.")
                break
            if status not in {"queued", "in_progress"}:
                raise RuntimeError(f"Statut inconnu : {status!r}. Revoir la documentation.")
            time.sleep(delay + random.random())
            delay = min(delay * 1.5, 10)
        else:
            raise TimeoutError("Délai local atteint. Reprendre SANS --submit ; la requête distante peut continuer.")
        url = result["video"]["url"]
        if urlsplit(url).scheme != "https":
            raise ValueError("URL vidéo non HTTPS.")
        # Le téléchargement n'envoie jamais les identifiants API au serveur de médias.
        output = directory / "video.mp4"
        if output.exists():
            raise FileExistsError(f"Vidéo déjà présente : {output}")
        temporary = directory / "video.mp4.part"
        with httpx.stream("GET", url, timeout=60, follow_redirects=True) as media:
            media.raise_for_status()
            with temporary.open("wb") as file:
                for chunk in media.iter_bytes():
                    file.write(chunk)
        if temporary.stat().st_size == 0:
            raise ValueError("Fichier téléchargé vide.")
        temporary.replace(output)
        print("Vidéo sauvegardée :", output)
        return output


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("directory", type=Path, help="Un dossier distinct par génération")
    parser.add_argument("--submit", action="store_true", help="Soumettre une nouvelle génération payante")
    parser.add_argument("--minutes", type=float, default=15, help="Durée maximale du suivi local")
    args = parser.parse_args()
    if args.minutes <= 0:
        parser.error("--minutes doit être strictement positif")
    run(args.directory, args.submit, args.minutes)


if __name__ == "__main__":
    main()
