# /// script
# requires-python = ">=3.11"
# dependencies = ["requests>=2.32,<3"]
# ///
"""Default: free estimate only. Paid creation requires --generate and confirmation."""
import argparse
import json
import os
import re
import time
from pathlib import Path
from urllib.parse import urljoin, urlparse

import requests

API_BASE = "https://api.entirefeed.com/v1"

PAYLOAD = json.loads("{\"task\":\"video.create\",\"model\":\"seedance-2\",\"input\":{\"prompt\":\"\\\"'SCENE 1: Earth from space, camera rapidly zooming in continuously — punching through white clouds, green landscape appears, patchwork fields getting closer, golden wheat field rushing up, camera keeps diving toward a single person standing in the middle of the field — a voiceover says \\\"did you know that most people have no idea about this\\\" — 2 seconds. SCENE 2: Seamless transition landing on a young guy in a white t-shirt standing in the wheat field, waist-high golden stalks around him, wind blowing his hair, he's already mid-sentence looking directly at a front-facing phone camera held at arm's length saying \\\"let me explain why this changes everything\\\", wide eyes, leaning into the camera, gesturing with his free hand. Bright afternoon sunlight, blue sky, raw selfie camera quality — 3 seconds. \\\"\",\"duration_seconds\":5,\"resolution\":\"720p\",\"aspect_ratio\":\"9:16\",\"generate_audio\":true}}")

def api(method, path, *, key=None, **kwargs):
    # Never forward the API key to a media download or another origin.
    url = urljoin(API_BASE + "/", path)
    if urlparse(url).netloc != urlparse(API_BASE).netloc or urlparse(url).scheme != "https":
        raise ValueError("Unexpected API URL")
    headers = kwargs.pop("headers", {})
    if key:
        headers["Authorization"] = f"Bearer {key}"
    response = requests.request(method, url, headers=headers, timeout=60, **kwargs)
    response.raise_for_status()
    return response.json()


def main():
    parser = argparse.ArgumentParser(description="Estimate, generate, and download a Seedance 2 video.")
    action = parser.add_mutually_exclusive_group()
    action.add_argument("--generate", action="store_true", help="Enable a paid request after confirmation.")
    parser.add_argument("--idempotency-key", help="Reuse this key to recover the same intended request.")
    action.add_argument("--resume", metavar="GENERATION_ID", help="Read an existing generation; creates no new work.")
    parser.add_argument("--output", type=Path, default=Path("seedance-demo.mp4"))
    args = parser.parse_args()
    key = os.environ.get("ENTIREFEED_API_KEY")

    if (args.generate or args.resume) and args.output.exists():
        raise SystemExit(f"{args.output} already exists. Choose another --output path.")

    if args.resume:
        if not key:
            raise SystemExit("Set ENTIREFEED_API_KEY to resume.")
        if not re.fullmatch(r"gen_[a-zA-Z0-9]+", args.resume):
            raise SystemExit("Expected a gen_... generation ID.")
        current = api("GET", f"generations/{args.resume}", key=key)
        status_url = f"{API_BASE}/generations/{args.resume}"
    else:
        estimate = api("POST", "estimate", json=PAYLOAD)
        print("Current estimate:", json.dumps(estimate["estimated_cost"], indent=2))
        if not args.generate:
            print("Estimate only: no media generated and no balance spent.")
            print("To generate, add --generate --idempotency-key YOUR_UNIQUE_REQUEST_KEY.")
            return
        if not key or not args.idempotency_key:
            raise SystemExit("Set ENTIREFEED_API_KEY and pass --idempotency-key before generating.")
        print("Creating reserves balance using the current price, which can change after this estimate.")
        if input("Type GENERATE to create this paid request: ").strip() != "GENERATE":
            print("No generation created.")
            return
        print(f"Request key: {args.idempotency_key}. Keep it to recover this request after a network error.")
        current = api(
            "POST", "generations", key=key, json=PAYLOAD,
            headers={"Idempotency-Key": args.idempotency_key},
        )
        status_url = current["links"]["self"]

    generation_id = current["id"]
    print(f"Generation: {generation_id}")
    print(f"Resume with: uv run seedance-2.py --resume {generation_id}")
    deadline = time.monotonic() + 900
    # Always fetch the complete record, even if create returned an immediate success.
    while True:
        current = api("GET", status_url, key=key)
        status = current["status"]
        print("Status:", status)
        if status not in {"queued", "running"}:
            break
        if time.monotonic() >= deadline:
            raise SystemExit(f"Still active. Resume {generation_id}; do not create another request.")
        time.sleep(5)

    if current["status"] != "succeeded":
        print(json.dumps(current.get("error"), indent=2))
        print("Usage:", json.dumps(current.get("usage"), indent=2))
        raise SystemExit(f"Stopped at {current['status']}. See Console for details.")

    assets = (current.get("output") or {}).get("assets", [])
    video = next((asset for asset in assets if asset.get("type") == "video"), None)
    if not video:
        raise SystemExit("Succeeded, but no video asset was returned.")
    if urlparse(video["url"]).scheme != "https":
        raise SystemExit("Unexpected download URL.")
    # A separate request keeps the API credential off the asset host.
    partial = args.output.with_name(args.output.name + ".part")
    # Only remove a partial file created by this attempt if the download fails.
    output = partial.open("xb")
    try:
        with output:
            with requests.get(video["url"], stream=True, timeout=120) as response:
                response.raise_for_status()
                for chunk in response.iter_content(chunk_size=1024 * 1024):
                    output.write(chunk)
    except (requests.RequestException, OSError):
        partial.unlink(missing_ok=True)
        raise
    partial.rename(args.output)
    print("Saved:", args.output)
    print("Usage:", json.dumps(current.get("usage"), indent=2))


if __name__ == "__main__":
    try:
        main()
    except requests.RequestException as error:
        raise SystemExit(
            f"Request failed: {error}. Resume the generation ID if available; "
            "otherwise reuse the same idempotency key and unchanged payload."
        ) from error
