#!/usr/bin/env python3
"""Playwright 录屏入口（薄壳，逻辑在 _media_screenshot 包）。

模式：
  - 固定时长 / 条件停止（--duration / --stop-when-*）
  - 自动滚动（--scroll-through）
  - 自定义分镜（--storyboard <json>）
  - 模板分镜（--template <name> --param k=v ...）
"""
from __future__ import annotations

import argparse
import json
import os
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
from _media_screenshot import (  # noqa: E402
    cli_args,
    do_record,
    do_storyboard,
    template,
    trim,
)
from _media_upload import finalize_recording  # noqa: E402


def main() -> None:
    ap = argparse.ArgumentParser(
        description="通过 Playwright Python API 对网页录屏，输出 .webm。"
    )
    ap.add_argument(
        "-u", "--url",
        help="待录制的页面 URL（如未指定，模板/storyboard 必须包含 url）",
    )
    ap.add_argument(
        "-o", "--output", default="recording.webm",
        help="本地输出 .webm 路径（默认 recording.webm）",
    )

    # plain recording stop conditions
    ap.add_argument("--duration", type=int, help="固定录制时长（毫秒）")
    ap.add_argument("--stop-when-selector", help="选择器出现就停")
    ap.add_argument("--stop-when-hidden", help="选择器消失就停")
    ap.add_argument(
        "--max-duration", type=int, default=60000,
        help="安全上限（ms），用 stop-when-* 时防挂死",
    )

    # scroll-through
    ap.add_argument(
        "--scroll-through", action="store_true",
        help="录制时自动从顶部平滑滚到底部",
    )
    ap.add_argument("--scroll-step", type=int, default=60)
    ap.add_argument("--scroll-interval", type=int, default=50)
    ap.add_argument("--scroll-pause-top", type=int, default=800)
    ap.add_argument("--scroll-pause-bottom", type=int, default=1200)

    # storyboard / template
    ap.add_argument("--storyboard", help="分镜 JSON 文件路径")
    ap.add_argument(
        "--template",
        help="使用 templates/<name>.json 模板，配合 --param 传参",
    )
    ap.add_argument(
        "--param", action="append", default=[], metavar="KEY=VALUE",
        help="给模板传参，可重复",
    )
    ap.add_argument(
        "--list-templates", action="store_true",
        help="列出可用模板并退出",
    )

    # 收尾（transcode + 封面 + VOD 上传，默认开；委托 ab-render /finalize）
    ap.add_argument(
        "--no-upload", action="store_true",
        help="录完不上传 VOD，只保留本地 webm（默认会转码+封面+上传并返回 CDN 地址）",
    )
    ap.add_argument("--vod-title", help="上传到 VOD 的标题（默认取输出文件名）")
    ap.add_argument(
        "--cover-at-sec", type=float, default=0.5,
        help="封面抽帧时间点（秒，默认 0.5，避开首帧白屏）",
    )
    ap.add_argument(
        "--keep-webm", action="store_true",
        help="上传成功后保留本地 webm（默认删除，VOD 已持有）",
    )

    cli_args.add_common_args(ap)

    args = ap.parse_args()

    if args.list_templates:
        for tpl in template.list_templates():
            print(tpl.stem)
        return

    if args.template and args.storyboard:
        print("--template 与 --storyboard 互斥，请二选一。", file=sys.stderr)
        sys.exit(2)

    if not (
        args.duration or args.stop_when_selector or args.stop_when_hidden
        or args.scroll_through or args.storyboard or args.template
    ):
        print(
            "需至少提供 --duration / --stop-when-selector / --stop-when-hidden "
            "/ --scroll-through / --storyboard / --template 之一。",
            file=sys.stderr,
        )
        sys.exit(2)

    if not args.output.lower().endswith(".webm"):
        print(
            "提示：Playwright 录屏输出为 webm，建议 --output 用 .webm 后缀。",
            file=sys.stderr,
        )

    storage_path, is_temp = cli_args.build_storage(args)

    cfg: dict = {
        "output": str(Path(args.output).resolve()),
        "browser": args.browser or "chromium",
        "maxDuration": args.max_duration,
    }
    if args.url:
        cfg["url"] = args.url
    cli_args.common_args_to_cfg(args, cfg)
    if storage_path:
        cfg["storageState"] = storage_path

    story: dict | None = None
    if args.template:
        params = template.parse_param_pairs(args.param)
        story = template.render_template(args.template, params)
    elif args.storyboard:
        try:
            story = json.loads(Path(args.storyboard).read_text())
        except (OSError, json.JSONDecodeError) as e:
            print(f"--storyboard 读取/解析失败：{e}", file=sys.stderr)
            sys.exit(2)

    if story is not None:
        for key in ("scenes", "transition"):
            if key in story:
                cfg[key] = story[key]
        for key in (
            "url", "viewport", "device", "colorScheme", "userAgent",
            "ignoreHttpsErrors", "waitForSelector", "waitForTimeout",
            "waitForReadySelectors", "settleMs",
        ):
            if key in story:
                cfg[key] = story[key]

    if args.duration:
        cfg["duration"] = args.duration
    if args.stop_when_selector:
        cfg["stopWhenSelector"] = args.stop_when_selector
    if args.stop_when_hidden:
        cfg["stopWhenHidden"] = args.stop_when_hidden
    if args.scroll_through:
        cfg["scrollThrough"] = True
        cfg["scrollStep"] = args.scroll_step
        cfg["scrollInterval"] = args.scroll_interval
        cfg["scrollPauseTop"] = args.scroll_pause_top
        cfg["scrollPauseBottom"] = args.scroll_pause_bottom

    if not cfg.get("url"):
        print(
            "必须提供 URL：通过 --url，或在模板 / storyboard JSON 里定义 url。",
            file=sys.stderr,
        )
        sys.exit(2)

    is_storyboard = story is not None
    print(
        f"[runner] mode={'storyboard' if is_storyboard else 'record'}",
        file=sys.stderr,
    )
    try:
        if is_storyboard:
            out, start_ms, end_ms = do_storyboard(cfg)
        else:
            out, start_ms, end_ms = do_record(cfg)
    finally:
        if is_temp and storage_path:
            try:
                os.unlink(storage_path)
            except OSError:
                pass

    if not out.exists():
        print(f"录屏未生成：{out}", file=sys.stderr)
        sys.exit(1)

    local_path = str(out)

    # 端锚裁剪窗口：保留内容窗口 + 边距（与 _media_screenshot/trim.py 的常量一致）。
    # 上传路径把它交给 ab-render 在转码同一 pass 里裁；--no-upload 单机路径才本地裁。
    keep_tail_sec = None
    if end_ms > start_ms:
        keep_tail_sec = ((end_ms - start_ms) + 80 + 200) / 1000.0

    # ── 收尾：转码 + 封面 + 上传 VOD（默认开）───────────────────────────────
    # ffmpeg / OSS 全部在 ab-render 侧完成，本 skill 只推文件 + 轮询状态。
    # 环境缺失（如裸跑 CLI 无 PRIV_TOKEN）或收尾失败 → 优雅降级为「仅本地文件」。
    upload_enabled = not args.no_upload
    priv_token = os.environ.get("PRIV_TOKEN", "")
    if upload_enabled and not priv_token:
        print("[record] 未设置 PRIV_TOKEN，跳过 VOD 上传，仅保留本地文件。", file=sys.stderr)
        upload_enabled = False

    if not upload_enabled:
        # 单机 / 不上传：没有 ab-render 帮忙裁，就在本地尽力裁一刀（需系统 ffmpeg，
        # 缺失则 trim.trim_video 保留原片并提示）。
        trim.trim_video(out, start_ms, end_ms)
        print(local_path)
        return

    def _on_progress(data: dict) -> None:
        # finalizeStatus 的 phase/progress → __progress__，喂 ab-agent SSE 时间线
        line: dict = {"__progress__": True, "phase": data.get("phase")}
        prog = data.get("progress")
        if isinstance(prog, (int, float)):
            line["progress"] = float(prog)
        print(json.dumps(line), flush=True)

    conversation_id = os.environ.get("CONVERSATION_ID") or None
    title = args.vod_title or Path(local_path).stem
    try:
        result = finalize_recording(
            local_path,
            private_token=priv_token,
            transcode=True,
            cover=True,
            cover_at_sec=args.cover_at_sec,
            title=title,
            resolve_cdn=True,
            keep_tail_sec=keep_tail_sec,
            conversation_id=conversation_id,
            on_progress=_on_progress,
        )
    except Exception as e:  # noqa: BLE001 — 收尾失败不应丢掉已录好的本地文件
        print(f"[record] VOD 上传收尾失败，降级为仅本地文件：{e}", file=sys.stderr)
        print(local_path)
        return

    cdn_url = result.get("fileUrl") or ""
    cover_url = result.get("coverUrl") or ""
    duration = result.get("durationSec")
    vod_ref = result.get("vodRef") or ""
    file_id = result.get("fileId")

    # 上传成功，默认删本地 webm（VOD 已持有），除非 --keep-webm
    if not args.keep_webm:
        try:
            os.unlink(local_path)
        except OSError:
            pass

    print("✅ 录屏已生成并上传 VOD")
    if cdn_url:
        print(f"🔗 视频地址：{cdn_url}")
    if cover_url:
        print(f"🖼️ 封面：{cover_url}")
    if isinstance(duration, (int, float)):
        print(f"🕐 时长：{duration:.1f}s")
    # 结构化 asset 标记：ab-agent postcall 解析这一行下发前端播放器
    asset = {
        "url": cdn_url,
        "coverUrl": cover_url,
        "durationSec": duration,
        "vodRef": vod_ref,
        "fileId": file_id,
    }
    print("__web_record_asset__ " + json.dumps(asset, ensure_ascii=False))


if __name__ == "__main__":
    main()
