#!/usr/bin/env python3
"""
Sophnet Infinite Talk (Digital Human) Script

Two-phase workflow:
  create: submit a task, return task_id
  query:  poll task status by task_id

Usage:
  python gen.py create --image-url <url> --audio-url <url> [--prompt <text>]
  python gen.py query  --task-id <id>
"""
import os
import sys
import argparse
import json
import requests
from typing import Optional, Any, Dict
import sophnet_tools

TASK_URL = "https://www.sophnet.com/api/open-apis/projects/easyllms/videogenerator/task"
DEFAULT_PROMPT = "自信的面对镜头"


def _is_url(path: str) -> bool:
    return path.startswith(("http://", "https://"))


def _resolve_to_url(path: str, label: str) -> str:
    """本地文件上传 OSS 转为 URL；已经是 URL 则直接返回。失败时抛出 ValueError。"""
    if _is_url(path):
        return path
    if not os.path.isfile(path):
        raise ValueError(f"{label}文件不存在: {path}")
    url = sophnet_tools.upload_oss(path)
    if not url:
        raise ValueError(f"{label}文件上传 OSS 失败: {path}")
    return url


def _headers(api_key: str) -> dict:
    return {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }


def create_task(
    api_key: str, image_url: str, audio_url: str, prompt: str = DEFAULT_PROMPT
) -> Dict[str, Any]:
    """提交数字人视频任务，返回 {"task_id": "..."} 或 {"error": "..."}。"""
    payload = {
        "model": "InfiniteTalk",
        "content": [
            {"type": "text", "text": prompt},
            {
                "type": "image_url",
                "image_url": {"url": image_url},
                "role": "first_frame",
            },
            {"type": "audio_url", "audio_url": audio_url},
        ],
    }
    try:
        resp = requests.post(
            TASK_URL, headers=_headers(api_key), json=payload, timeout=60
        )
        resp.raise_for_status()
        task_id = resp.text.strip()
        if task_id:
            return {"task_id": task_id}
        return {"error": f"未能从响应中解析 task_id: {task_id}"}
    except requests.exceptions.RequestException as e:
        return {"error": f"HTTP 请求失败: {e}"}


def _normalize_video_url(raw: Any) -> Optional[str]:
    """API 在排队时可能返回字符串 'None'，视为无效。"""
    if raw is None:
        return None
    s = str(raw).strip()
    return None if s in ("", "None", "none") else s


def get_task(api_key: str, task_id: str) -> Dict[str, Any]:
    """
    查询任务状态。

    返回:
      进行中: {"status": "processing", "video_url": null}
      成功:   {"status": "succeeded", "video_url": "<url>"}
      失败:   {"status": "failed", "error": "<reason>"}
    """
    try:
        resp = requests.get(
            f"{TASK_URL}/{task_id}", headers=_headers(api_key), timeout=30
        )
        resp.raise_for_status()
        data = resp.json()
    except requests.exceptions.RequestException as e:
        return {"error": f"HTTP 请求失败: {e}"}

    status = (data.get("status") or "").strip()
    video_url = _normalize_video_url((data.get("content") or {}).get("video_url"))

    if status == "succeeded":
        if video_url:
            return {"status": "succeeded", "video_url": video_url}
        return {"status": "failed", "error": "任务标记为成功但未返回有效视频地址"}

    if "failed" in status.lower():
        err = (
            data.get("message")
            or data.get("error")
            or (data.get("content") or {}).get("error")
            or status
        )
        return {"status": "failed", "error": str(err)}

    return {"status": status, "video_url": video_url}


def _json_out(obj: Dict[str, Any]) -> None:
    print(json.dumps(obj, ensure_ascii=False))


if __name__ == "__main__":
    api_key = sophnet_tools.get_api_key()
    if api_key is None:
        _json_out(
            {"error": "未找到 Sophnet API Key，请联系客户支持"}
        )
        sys.exit(1)

    parser = argparse.ArgumentParser(description="Sophnet Infinite Talk (数字人)")
    sub = parser.add_subparsers(dest="command", required=True)

    p_create = sub.add_parser("create", help="创建数字人视频任务")
    p_create.add_argument(
        "--image-url", required=True, help="人物形象图片 URL 或本地路径"
    )
    p_create.add_argument(
        "--audio-url", required=True, help="驱动音频 URL 或本地路径"
    )
    p_create.add_argument(
        "--prompt",
        default=DEFAULT_PROMPT,
        help=f"动作/表情提示词（默认: {DEFAULT_PROMPT}）",
    )

    p_query = sub.add_parser("query", help="查询数字人任务状态")
    p_query.add_argument("--task-id", required=True, help="任务 ID")

    args = parser.parse_args()

    if args.command == "create":
        try:
            image_url = _resolve_to_url(args.image_url, "图片")
            audio_url = _resolve_to_url(args.audio_url, "音频")
        except ValueError as e:
            _json_out({"error": str(e)})
            sys.exit(1)
        result = create_task(api_key, image_url, audio_url, args.prompt)
        _json_out(result)
        if "error" in result:
            sys.exit(1)

    elif args.command == "query":
        result = get_task(api_key, args.task_id)
        _json_out(result)
        if "error" in result:
            sys.exit(1)
