#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
AI Influencer Video Üretici - LTX Desktop (Prompt-driven TTS + Lip-sync)
========================================================================
Metin + karakter resmi (+ opsiyonel ürün resimleri) → konuşan/tanıtan AI influencer videosu.

LTX Desktop kendi ses üretir + lip-sync yapar. Prompt'ta:
    `the woman says in Turkish "Selam ben ..."` denildiğinde otomatik konuşur.

Pipeline:
1. Metin cümlelere bölünür (ürün sayısı varsa eşit dağılım)
2. Her segment için karakter (+o segmentin ürünü) PIL ile yan yana compose edilir
3. LTX `/api/generate` ses-üretmeli prompt ile çağrılır → segment video (ses dahil)
4. Segmentler ffmpeg ile birleştirilir (LTX kendi ses kattığı için ek ses eklenmez)

Mevcut Muzik-Video-Uret pipeline'ı dokunulmaz; sadece bazı helper'lar import edilir.

Kullanım:
    python ai_influencer_uret.py --metin "Bugun size Sunsilk sampuanini..." \
                                  --karakter ./char.png --urun ./parfum.png ./krem.png \
                                  --karakter-cinsiyet woman --dil Turkish
"""

import os
import sys
import json
import time
import shutil
import argparse
import subprocess
import urllib.request
import urllib.error
from pathlib import Path
from datetime import datetime

SCRIPT_DIR = Path(__file__).parent
sys.path.insert(0, str(SCRIPT_DIR))
import muzik_video_uret as MV

DEFAULT_RESOLUTION = "540p"
DEFAULT_ASPECT = "16:9"
DEFAULT_GUIDANCE = 3.5
DEFAULT_STEPS = 30
DEFAULT_DURATION = 10  # her segment kaç saniye (LTX'in izin verdiği değer)
DEFAULT_GENDER = "woman"  # woman | man | person
DEFAULT_LANG = "Turkish"

DEFAULT_BASE_PROMPT = (
    "professional AI influencer presenting product to camera, studio lighting, "
    "clean background, expressive face, natural mouth movement matching speech, "
    "shallow depth of field, cinematic 4k"
)


def ensure_pillow():
    try:
        from PIL import Image  # noqa
    except ImportError:
        print("[*] Pillow kuruluyor...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "-q", "pillow"])


def split_text_into_segments(text, n):
    """Metni n parçaya böl. Cümle sınırlarına saygı duy."""
    text = text.strip()
    if n <= 1:
        return [text]
    import re
    sentences = re.split(r'(?<=[.!?…])\s+', text)
    sentences = [s.strip() for s in sentences if s.strip()]
    if len(sentences) <= n:
        # eşit kelime dağılımına geç
        words = text.split()
        per = max(1, len(words) // n)
        chunks = []
        for i in range(n):
            start = i * per
            end = (i + 1) * per if i < n - 1 else len(words)
            chunks.append(" ".join(words[start:end]))
        return chunks
    # cümleleri n bucket'a dağıt
    per = len(sentences) / n
    chunks = []
    for i in range(n):
        start = int(i * per)
        end = int((i + 1) * per) if i < n - 1 else len(sentences)
        chunks.append(" ".join(sentences[start:end]))
    return chunks


def compose_character_product(char_path, product_path, out_path,
                              canvas_w=1280, canvas_h=720, char_ratio=0.55):
    from PIL import Image
    canvas = Image.new("RGB", (canvas_w, canvas_h), (15, 15, 20))
    char = Image.open(char_path).convert("RGB")
    prod = Image.open(product_path).convert("RGB")
    left_w = int(canvas_w * char_ratio)
    right_w = canvas_w - left_w
    char_fit = _fit(char, left_w, canvas_h)
    prod_fit = _fit(prod, right_w - 40, canvas_h - 80)
    canvas.paste(char_fit, _center_paste(char_fit, 0, 0, left_w, canvas_h))
    canvas.paste(prod_fit, _center_paste(prod_fit, left_w, 0, right_w, canvas_h))
    canvas.save(out_path, quality=95)
    return out_path


def _fit(img, max_w, max_h):
    from PIL import Image
    w, h = img.size
    scale = min(max_w / w, max_h / h)
    return img.resize((int(w * scale), int(h * scale)), Image.LANCZOS)


def _center_paste(img, x, y, box_w, box_h):
    w, h = img.size
    return (x + (box_w - w) // 2, y + (box_h - h) // 2)


def fit_single(image_path, out_path, canvas_w=1280, canvas_h=720):
    from PIL import Image
    canvas = Image.new("RGB", (canvas_w, canvas_h), (15, 15, 20))
    img = Image.open(image_path).convert("RGB")
    fit = _fit(img, canvas_w, canvas_h)
    canvas.paste(fit, _center_paste(fit, 0, 0, canvas_w, canvas_h))
    canvas.save(out_path, quality=95)
    return out_path


def generate_video_tts(api_url, prompt, image_path, token, duration,
                       resolution, aspect, audio_path=None,
                       guidance_scale=None, steps=None, neg_prompt=None):
    """LTX /api/generate — UI ile bire bir aynı payload.
    audio_path None → model='fast', audioPath yok (prompt-TTS modu)
    audio_path verili → model='pro', audioPath dolu (mevcut sese lip-sync)
    """
    image_abs = str(Path(image_path).resolve()).replace("\\", "/")
    use_audio = bool(audio_path)
    model = "pro" if use_audio else "fast"
    payload = {
        "prompt": prompt,
        "model": model,
        "duration": str(duration),
        "resolution": resolution,
        "fps": MV.DEFAULT_FPS,
        "audio": "true",
        "cameraMotion": "none",
        "aspectRatio": aspect,
        "imagePath": image_abs,
    }
    if use_audio:
        payload["audioPath"] = str(Path(audio_path).resolve()).replace("\\", "/")
    print(f"  [LTX] dur={duration}s res={resolution} model={model} audio={use_audio}")
    try:
        result = MV.ltx_api_request(api_url, "/api/generate", method="POST",
                                    data=payload, token=token)
    except urllib.error.HTTPError as e:
        body = e.read().decode()
        print(f"  HATA: HTTP {e.code}:")
        print(body)
        return None
    except Exception as e:
        print(f"  HATA: {e}")
        return None

    status = result.get("status", "")
    video_path = result.get("video_path", "")
    if status == "complete" and video_path:
        return video_path
    print(f"  HATA: status={status} resp={json.dumps(result)[:300]}")
    return None


def main():
    p = argparse.ArgumentParser(description="AI Influencer Video Üretici (Prompt-TTS)")
    p.add_argument("--metin", required=True, help="Prompt metni veya .txt dosyası (LTX'e direkt geçer)")
    p.add_argument("--karakter", required=True, help="Karakter resmi (png/jpg)")
    p.add_argument("--urun", nargs="*", default=[], help="Bir veya daha fazla ürün resmi")
    p.add_argument("--resolution", "-r", default=DEFAULT_RESOLUTION,
                   choices=["512p", "540p", "720p", "1080p"])
    p.add_argument("--aspect", "-a", default=DEFAULT_ASPECT, choices=["16:9", "9:16"])
    p.add_argument("--guidance-scale", "-g", type=float, default=DEFAULT_GUIDANCE)
    p.add_argument("--steps", "-n", type=int, default=DEFAULT_STEPS)
    p.add_argument("--segment-duration", "-s", type=int, default=DEFAULT_DURATION,
                   help="Her segment kaç saniye olsun (LTX 5/6/8/10/20)")
    p.add_argument("--output", "-o", default=None)
    p.add_argument("--port", type=int, default=None)
    p.add_argument("--token", default=None)
    p.add_argument("--face-swap", default=None)
    p.add_argument("--ses", default=None,
                   help="Opsiyonel ses dosyasi (mp3/wav). Verilirse model=pro, lip-sync mevcut sese.")
    args = p.parse_args()

    # Metin
    metin = args.metin
    if os.path.isfile(metin) and metin.lower().endswith(".txt"):
        metin = Path(metin).read_text(encoding="utf-8").strip()
    if not metin:
        print("HATA: Metin boş!"); sys.exit(1)

    char_path = Path(args.karakter)
    if not char_path.is_file():
        print(f"HATA: Karakter resmi yok: {char_path}"); sys.exit(1)

    urun_paths = [Path(x) for x in args.urun if Path(x).is_file()]
    print(f"[*] Karakter: {char_path.name}")
    print(f"[*] Ürün sayısı: {len(urun_paths)}")
    print(f"[*] Metin uzunluğu: {len(metin)} karakter")

    # Segment sayısı: ürün sayısı kadar; ürün yoksa metni ~100 kelime/segmentte böl
    if urun_paths:
        seg_count = len(urun_paths)
    else:
        words = len(metin.split())
        seg_count = max(1, (words + 80) // 80)

    seg_dur = MV.snap_to_allowed_duration(args.segment_duration, args.resolution)
    seg_dur = max(5, seg_dur)
    print(f"[*] Segment: {seg_count} × {seg_dur}s")

    # Çalışma dizini
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")
    work_dir = SCRIPT_DIR / "output" / f"influencer_{ts}"
    work_dir.mkdir(parents=True, exist_ok=True)
    frames_dir = work_dir / "frames"; frames_dir.mkdir()
    seg_dir = work_dir / "segments"; seg_dir.mkdir()
    out_path = Path(args.output) if args.output else (work_dir / f"influencer_{ts}.mp4")

    ensure_pillow()

    # Metni segmentlere böl
    metin_parcalari = split_text_into_segments(metin, seg_count)
    for i, t in enumerate(metin_parcalari):
        print(f"  Seg{i+1} metin: {t[:80]}{'...' if len(t)>80 else ''}")

    # LTX backend
    print(f"\n[*] LTX backend tespit...")
    api_url = f"http://127.0.0.1:{args.port}" if args.port else MV.find_ltx_backend_port()
    token = args.token or MV.discover_auth_token(api_url)
    if token:
        (SCRIPT_DIR / "ltx_token.txt").write_text(token)
    MV.wait_for_idle(api_url, token)

    # Görselleri oluştur (ve opsiyonel face swap)
    canvas_w, canvas_h = (1280, 720) if args.aspect == "16:9" else (720, 1280)
    composed = []
    for i in range(seg_count):
        outp = frames_dir / f"input_{i:03d}.jpg"
        if urun_paths:
            urun = urun_paths[i % len(urun_paths)]
            compose_character_product(char_path, urun, outp, canvas_w, canvas_h)
        else:
            fit_single(char_path, outp, canvas_w, canvas_h)
        composed.append(outp)

    if args.face_swap and Path(args.face_swap).is_file():
        print("[*] Face swap uygulanıyor...")
        for i, img in enumerate(composed):
            sw = frames_dir / f"input_{i:03d}_sw.jpg"
            if MV.face_swap_image(args.face_swap, img, sw):
                composed[i] = sw

    # Üretim
    print(f"\n[*] Video segmentleri üretiliyor...")
    video_segs = []
    start = time.time()

    for i in range(seg_count):
        full_prompt = metin_parcalari[i]
        print(f"\n[SEG {i+1}/{seg_count}] prompt: {full_prompt[:100]}")
        MV.wait_for_idle(api_url, token)
        vp = generate_video_tts(
            api_url=api_url,
            prompt=full_prompt,
            image_path=composed[i],
            audio_path=args.ses,
            token=token,
            duration=seg_dur,
            resolution=args.resolution,
            aspect=args.aspect,
            guidance_scale=args.guidance_scale,
            steps=args.steps,
        )
        if vp is None:
            print(f"  HATA: segment {i+1} atlandı"); continue
        seg_out = seg_dir / f"segment_{i:03d}.mp4"
        shutil.copy2(vp, seg_out)
        video_segs.append(seg_out)
        elapsed = time.time() - start
        print(f"  ✓ {elapsed:.0f}s | ort {elapsed/(i+1):.1f}s/seg")

    if not video_segs:
        print("HATA: Hiç segment üretilemedi"); sys.exit(1)

    # Birleştir (LTX zaten ses katıyor — direkt concat)
    print(f"\n[*] Birleştiriliyor ({len(video_segs)} segment)...")
    if len(video_segs) == 1:
        shutil.copy2(video_segs[0], out_path)
    else:
        MV.concatenate_videos(video_segs, out_path)

    total = time.time() - start
    size_mb = out_path.stat().st_size / (1024 * 1024)
    print(f"\n{'='*60}")
    print(f"  TAMAMLANDI!")
    print(f"  Çıkış : {out_path}")
    print(f"  Boyut : {size_mb:.1f} MB")
    print(f"  Süre  : {total:.0f}s ({total/60:.1f} dk)")
    print(f"{'='*60}\n")
    return str(out_path)


if __name__ == "__main__":
    main()
