"""Ví dụ: tạo một tập phim từ ý tưởng bằng API của LIN AI, từ đầu tới lúc tải video về.

    pip install requests
    export LIN_AI_URL="https://<địa chỉ trang LIN AI>"
    export LIN_AI_KEY="lin_..."            # tạo ở trang API trong studio
    python create_episode.py "Thỏ và Rùa chạy thi lần hai, kết thúc bất ngờ."

Script đi đúng các bước bạn bấm trên trang web: viết kịch bản, tạo tập, tạo và duyệt ảnh nhân vật,
gửi dựng (bước duy nhất trừ linh thạch), chờ ghép, tải video.
"""
import os
import sys
import time

import requests

BASE = os.environ["LIN_AI_URL"].rstrip("/")
session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['LIN_AI_KEY']}"


def call(method: str, path: str, **kwargs):
    """Gọi một đường API. Lỗi thì dừng và in đúng câu máy chủ trả về."""
    res = session.request(method, BASE + path, timeout=120, **kwargs)
    if res.status_code >= 400:
        raise SystemExit(f"{method} {path} -> {res.status_code}: {res.text}")
    return res.json()


def episode(episode_id: str) -> dict:
    return call("GET", f"/api/episodes/{episode_id}")


idea = sys.argv[1] if len(sys.argv) > 1 else "Thỏ và Rùa chạy thi lần hai, kết thúc bất ngờ, có bài học về tôn trọng đối thủ."

# 1. Hệ thống viết kịch bản (JSON prompt) từ ý tưởng. Miễn phí.
draft = call("POST", "/api/episodes/draft", json={"idea": idea, "sceneCount": 5, "voiceGender": "female"})
errors = [i["message"] for i in draft["issues"] if i["level"] == "error"]
if errors:
    raise SystemExit(f"Kịch bản còn lỗi: {errors}")

# 2. Tạo tập từ kịch bản đó. Chưa trừ gì. Muốn sửa kịch bản thì sửa draft["json"] trước khi gửi.
episode_id = call(
    "POST",
    "/api/episodes",
    json={"json": draft["json"], "idea": idea, "aspect": "9:16", "voiceGender": "female", "captions": True},
)["id"]
print("Đã tạo tập", episode_id)

# 3. Ảnh nhân vật: xin tạo ảnh cho nhân vật chưa có, chờ vẽ xong, rồi duyệt. Miễn phí.
for person in episode(episode_id)["episode"]["cast"]:
    if person["status"] == "no_image":
        call("POST", f"/api/characters/episode/{person['id']}/image")
while True:
    cast = episode(episode_id)["episode"]["cast"]
    for person in cast:
        if person["status"] == "pending_review":
            call("POST", f"/api/characters/episode/{person['id']}/approve")
    if all(person["status"] == "approved" for person in cast):
        break
    if any(person["status"] == "no_image" for person in cast):
        raise SystemExit("Có nhân vật không vẽ được ảnh; xem trang tập để biết lý do.")
    time.sleep(5)
print("Nhân vật đã có ảnh được duyệt")

# 4. Gửi mọi cảnh nháp đi dựng. ĐÂY là bước trừ linh thạch; báo giá nằm ở quote.total.
quote = episode(episode_id)["quote"]
print(f"Sẽ trừ {quote['total']} linh thạch, số dư {quote['balance']}")
sent = call("POST", f"/api/episodes/{episode_id}/render", json={})
print(f"Đã trừ {sent['charged']} cho {sent['scenes']} cảnh, còn {sent['balance']}")

# 5. Chờ dựng và ghép. Cảnh xong hết thì hệ thống tự ghép thành một video.
while True:
    current = episode(episode_id)["episode"]
    done = sum(1 for scene in current["scenes"] if scene["status"] in ("rendered", "approved"))
    print(f"  {current['status']}: {done}/{len(current['scenes'])} cảnh")
    if current["status"] == "done":
        break
    if current["status"] == "partial":
        failed = [scene["index"] for scene in current["scenes"] if scene["status"] == "failed"]
        raise SystemExit(f"Cảnh {failed} bị lỗi (đã hoàn linh thạch). Gửi lại bằng sceneIds, xem tài liệu.")
    time.sleep(10)

# 6. Tải video. Đường này chuyển hướng tới link tải có hạn dùng ngắn.
video = session.get(f"{BASE}/api/episodes/{episode_id}/download", timeout=300)
video.raise_for_status()
out = f"tap-{episode_id[:8]}.mp4"
with open(out, "wb") as f:
    f.write(video.content)
print("Đã tải", out, f"({len(video.content) // 1024} KB)")
