#!/usr/bin/env python3
"""
Geração de B-roll via IA usando Seedance-1-Pro (ByteDance) no Replicate.

API: https://replicate.com/bytedance/seedance-1-pro
Custo aproximado: US$0.05/segundo @ 1080p, ~US$0.025/segundo @ 720p

Uso típico (substitui Pexels):
    clips = gerar_videos_seedance(
        termos=[{"query": "...", "inicio": 0.0, "fim": 5.0}, ...],
        job_id="abc",
        temp_dir=Path("/tmp/temp"),
        resolution="720p",
    )
    # clips => [{"path": "/tmp/...mp4", "inicio": 0.0, "fim": 5.0}, ...]
"""

from __future__ import annotations

import os
import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path
from typing import Iterable

import requests


# ---------------------------------------------------------------------------
# Env loader (carrega REPLICATE_API_TOKEN do /opt/mia/.env se não setado)
# ---------------------------------------------------------------------------
def _load_replicate_token() -> str:
    tok = os.getenv("REPLICATE_API_TOKEN", "").strip()
    if tok:
        return tok
    env_path = Path("/opt/mia/.env")
    if env_path.exists():
        try:
            for line in env_path.read_text(encoding="utf-8").splitlines():
                line = line.strip()
                if not line or line.startswith("#"):
                    continue
                if "=" not in line:
                    continue
                k, v = line.split("=", 1)
                if k.strip() == "REPLICATE_API_TOKEN":
                    v = v.strip().strip('"').strip("'")
                    os.environ["REPLICATE_API_TOKEN"] = v
                    return v
        except Exception as e:
            print(f"[seedance] erro lendo /opt/mia/.env: {e}")
    return ""


REPLICATE_TOKEN = _load_replicate_token()
MODEL_SLUG = "bytedance/seedance-1-pro"

# Limites do Seedance
SEEDANCE_DUR_MIN = 3
SEEDANCE_DUR_MAX = 12
# Pra deixar previsível: cada clip de B-roll fica entre 3 e 5s
SEEDANCE_DUR_DEFAULT = 5


def _enriquecer_prompt(query: str) -> str:
    """
    Recebe a query curta vinda do Claude (ex: "person typing on laptop")
    e devolve um prompt rico otimizado para Seedance, vertical 9:16.
    """
    q = (query or "").strip().rstrip(".")
    if not q:
        return ""
    # Template hardcoded - barato, previsível e suficiente.
    # Foca em direção cinematográfica, luz natural, formato vertical e qualidade alta.
    return (
        f"{q}, cinematic vertical 9:16 composition, soft natural lighting, "
        f"shallow depth of field, smooth slow camera movement, ultra-realistic, "
        f"high detail photographic quality, 4k, professional color grading"
    )


def _baixar_arquivo(url: str, destino: Path, timeout: int = 120) -> bool:
    """Baixa um MP4 pra o caminho destino. Retorna True/False."""
    try:
        with requests.get(url, stream=True, timeout=timeout) as r:
            r.raise_for_status()
            destino.parent.mkdir(parents=True, exist_ok=True)
            with open(destino, "wb") as f:
                for chunk in r.iter_content(chunk_size=64 * 1024):
                    if chunk:
                        f.write(chunk)
        return destino.exists() and destino.stat().st_size > 0
    except Exception as e:
        print(f"[seedance] erro baixando {url}: {e}")
        return False


def _resolver_output_url(output) -> str | None:
    """
    O Replicate Python client retorna:
    - str (URL) na maioria dos modelos novos
    - objeto FileOutput (com método .url) em alguns
    - list[str] em modelos multi-output
    Normaliza pra URL única.
    """
    if output is None:
        return None
    if isinstance(output, str):
        return output
    if isinstance(output, (list, tuple)) and output:
        return _resolver_output_url(output[0])
    # FileOutput tem .url() ou atributo .url
    for attr in ("url",):
        v = getattr(output, attr, None)
        if callable(v):
            try:
                return v()
            except Exception:
                pass
        elif isinstance(v, str):
            return v
    # último recurso: str() do objeto
    try:
        s = str(output)
        if s.startswith("http"):
            return s
    except Exception:
        pass
    return None


def _gerar_um_clip(
    termo: dict,
    idx: int,
    job_id: str,
    temp_dir: Path,
    resolution: str,
    aspect_ratio: str,
    fps: int,
) -> dict | None:
    """
    Gera UM clip via Seedance, baixa o MP4 e devolve dict do clip.
    Retorna None em caso de falha.
    """
    import replicate  # importa só no worker pra evitar custo em import-time

    query = (termo.get("query") or "").strip()
    if not query:
        return None

    prompt = _enriquecer_prompt(query)
    duracao_alvo = max(SEEDANCE_DUR_MIN, min(
        int(round(float(termo.get("fim", 0)) - float(termo.get("inicio", 0)))) or SEEDANCE_DUR_DEFAULT,
        SEEDANCE_DUR_DEFAULT,  # cap em 5s pra controlar custo
    ))

    t0 = time.time()
    print(f"[seedance] gerando clip {idx} ({duracao_alvo}s, {resolution}): {query!r}")

    try:
        output = replicate.run(
            MODEL_SLUG,
            input={
                "prompt": prompt,
                "duration": duracao_alvo,
                "resolution": resolution,
                "aspect_ratio": aspect_ratio,
                "fps": fps,
                "camera_fixed": False,
            },
        )
    except Exception as e:
        print(f"[seedance] erro chamando modelo (clip {idx}, '{query}'): {e}")
        return None

    url = _resolver_output_url(output)
    if not url:
        print(f"[seedance] clip {idx} sem URL no output: {output!r}")
        return None

    dest = temp_dir / f"{job_id}_seedance_{idx:02d}.mp4"
    if not _baixar_arquivo(url, dest):
        print(f"[seedance] falha ao baixar clip {idx} de {url}")
        return None

    dur = time.time() - t0
    print(f"[seedance] clip {idx} OK em {dur:.1f}s -> {dest}")

    return {
        "path": str(dest),
        "inicio": float(termo["inicio"]),
        "fim": float(termo["fim"]),
        "query": query,
        "duracao_gerada": duracao_alvo,
        "tempo_geracao_s": round(dur, 1),
    }


def gerar_videos_seedance(
    termos: Iterable[dict],
    job_id: str,
    temp_dir: Path,
    resolution: str = "720p",
    aspect_ratio: str = "9:16",
    fps: int = 24,
    max_workers: int = 3,
) -> list:
    """
    Gera N clips em paralelo via Seedance-1-Pro.

    Args:
        termos: lista de {"query": str, "inicio": float, "fim": float}
        job_id: id do job (pra nomear arquivos)
        temp_dir: pasta destino dos MP4s
        resolution: "720p" (mais barato) ou "1080p"
        aspect_ratio: "9:16" pra Reels (default)
        fps: 24 ou 30
        max_workers: paralelismo (default 3 — Replicate aceita bem)

    Returns:
        list[dict] = [{"path", "inicio", "fim", "query", "duracao_gerada", "tempo_geracao_s"}, ...]
        Lista vazia se TODAS falharem.
    """
    termos = [t for t in termos if (t.get("query") or "").strip()]
    if not termos:
        print("[seedance] nenhum termo válido recebido.")
        return []

    if not REPLICATE_TOKEN:
        print("[seedance] REPLICATE_API_TOKEN não configurado. Abortando.")
        return []

    # Garante que a env var está disponível pro client interno do replicate
    os.environ["REPLICATE_API_TOKEN"] = REPLICATE_TOKEN

    temp_dir = Path(temp_dir)
    temp_dir.mkdir(parents=True, exist_ok=True)

    t_start = time.time()
    print(f"[seedance] iniciando geração de {len(termos)} clip(s), "
          f"workers={max_workers}, resolution={resolution}")

    resultados: list = []
    with ThreadPoolExecutor(max_workers=max_workers) as pool:
        futures = {
            pool.submit(
                _gerar_um_clip, termo, i, job_id, temp_dir,
                resolution, aspect_ratio, fps,
            ): i
            for i, termo in enumerate(termos)
        }
        for fut in as_completed(futures):
            i = futures[fut]
            try:
                r = fut.result()
                if r:
                    resultados.append(r)
            except Exception as e:
                print(f"[seedance] worker {i} explodiu: {e}")

    # Ordena por timestamp pra montagem ficar previsível
    resultados.sort(key=lambda x: x["inicio"])

    total = time.time() - t_start
    sucesso = len(resultados)
    falhas = len(termos) - sucesso
    print(f"[seedance] terminou: {sucesso} OK, {falhas} falha(s) em {total:.1f}s total")

    return resultados


def estimar_custo_usd(qtd_clips: int, duracao_s: int = SEEDANCE_DUR_DEFAULT,
                     resolution: str = "1080p") -> float:
    """
    Estimativa simples de custo do Replicate.
    1080p ~= $0.05/s, 720p ~= $0.025/s
    """
    preco_s = 0.05 if resolution == "1080p" else 0.025
    return round(qtd_clips * duracao_s * preco_s, 2)


if __name__ == "__main__":
    # Smoke test: gera 1 clip curto e devolve o path
    import sys, tempfile, uuid
    termos = [{
        "query": sys.argv[1] if len(sys.argv) > 1 else "a photographer adjusting camera lens",
        "inicio": 0.0,
        "fim": 3.0,
    }]
    out = gerar_videos_seedance(
        termos=termos,
        job_id=uuid.uuid4().hex[:8],
        temp_dir=Path(tempfile.gettempdir()),
        resolution="720p",
    )
    print("RESULTADO:", out)
