#!/usr/bin/env python3
"""Набрать настоящие VFX-элементы со стоков, отсеяв обычную съёмку автоматически.

Профильные VFX-сайты — MyCreativeFX, FX Elements, ProductionCrate — отдают файлы
только после регистрации, поэтому программно оттуда не взять ничего. Зато на
обычных стоках элементы **есть**: их немного, они тонут в выдаче, и отличить их
по тегам невозможно — на запрос «arrow overlay» приходит видео, где кто-то снял
улицу.

Отличить можно по самому файлу. `render_element_overlay.detect_mode` смотрит на
`pix_fmt` и на цвет рамки кадра и говорит, элемент это или съёмка. Значит,
стратегия простая: искать широко, качать много, оставлять только то, что прошло
детектор.

Из тридцати скачанных остаётся обычно пять-восемь. Это нормально и это причина,
по которой отбор надо делать машиной: перебирать тридцать файлов глазами ради
шести никто не будет.
"""
from __future__ import annotations

import argparse
import json
import shutil
import subprocess
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))

from render_element_overlay import detect_mode  # noqa: E402
from skill_config import ASSETS, emit, require_binary, utf8_stdout  # noqa: E402

HERE = Path(__file__).resolve().parent
LIBRARY = ASSETS / "elements"

# Запросы, по которым на обычных стоках вообще попадаются элементы. Слова
# «black background», «alpha», «green screen» работают лучше названия эффекта:
# они описывают то, как файл сделан, а стоковые авторы это в теги выносят.
QUERIES: dict[str, list[str]] = {
    "particles": ["particles black background", "dust particles overlay black",
                  "sparks black background", "bokeh particles dark"],
    "glitch": ["glitch black background", "digital distortion black background",
               "vhs glitch overlay", "static noise black background"],
    "light": ["light leak black background", "lens flare black background",
              "flash light transition black", "god rays black background"],
    "smoke": ["smoke black background", "fog overlay black background",
              "ink drop black background"],
    "energy": ["energy lines black background", "electricity black background",
               "hud circle black background", "digital grid black background"],
    "green": ["green screen arrow", "green screen transition",
              "green screen frame element", "chroma key element"],
}


def main() -> None:
    utf8_stdout()
    parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
    parser.add_argument("--families", default=",".join(QUERIES),
                        help="Какие семьи собирать: " + ", ".join(QUERIES))
    parser.add_argument("--per-query", type=int, default=4)
    parser.add_argument("--providers", default="pixabay,pexels")
    parser.add_argument("--orientation", default="portrait")
    parser.add_argument("--duration", default="2-20")
    parser.add_argument("--out", default=str(LIBRARY))
    parser.add_argument("--keep-rejects", action="store_true",
                        help="Не удалять отклонённые файлы (для разбора)")
    parser.add_argument("--search-only", action="store_true")
    args = parser.parse_args()

    require_binary("ffmpeg")
    out_root = Path(args.out).resolve()
    staging = out_root / "_staging"
    staging.mkdir(parents=True, exist_ok=True)

    wanted = [f.strip() for f in args.families.split(",") if f.strip() in QUERIES]
    if not wanted:
        raise SystemExit(f"Нет таких семей. Есть: {', '.join(QUERIES)}")

    shortlist: dict[str, list[str]] = {}
    for family in wanted:
        for query in QUERIES[family]:
            done = subprocess.run(
                [sys.executable, str(HERE / "stock_media.py"), query,
                 "--kind", "video", "--orientation", args.orientation,
                 "--count", str(args.per_query * 3), "--duration", args.duration,
                 "--providers", args.providers],
                capture_output=True, text=True, errors="replace")
            try:
                payload = json.loads(done.stdout or "{}")
            except json.JSONDecodeError:
                continue
            picks = [item["pick"] for item in (payload.get("shortlist") or [])][:args.per_query]
            if picks:
                shortlist[query] = picks

    if args.search_only:
        emit({"status": "ok", "mode": "search", "queries": shortlist,
              "candidates": sum(len(v) for v in shortlist.values())})
        return

    for query, picks in shortlist.items():
        subprocess.run(
            [sys.executable, str(HERE / "stock_media.py"), query,
             "--kind", "video", "--orientation", args.orientation,
             "--count", str(args.per_query * 3), "--duration", args.duration,
             "--providers", args.providers, "--ids", ",".join(picks),
             "--out", str(staging)],
            capture_output=True, text=True, errors="replace")

    kept: list[dict] = []
    rejected: list[dict] = []
    for item in sorted(staging.rglob("*")):
        if item.suffix.lower() not in (".mp4", ".mov", ".webm", ".mkv"):
            continue
        mode, facts = detect_mode(item)
        record = {"file": item.name, "mode": mode, **facts}
        if mode == "unknown":
            rejected.append(record)
            if not args.keep_rejects:
                item.unlink(missing_ok=True)
            continue
        family_dir = out_root / mode
        family_dir.mkdir(parents=True, exist_ok=True)
        target = family_dir / item.name
        if not target.exists():
            shutil.move(str(item), str(target))
        record["path"] = str(target)
        kept.append(record)

    index_path = out_root / "index.json"
    existing = (json.loads(index_path.read_text(encoding="utf-8"))
                if index_path.is_file() else {"version": "1.0", "elements": []})
    known = {entry.get("path") for entry in existing["elements"]}
    existing["elements"] += [entry for entry in kept if entry.get("path") not in known]
    existing["how_to_use"] = (
        "black — складывается режимом screen; green — вырезается хромакеем по "
        "измеренному цвету; alpha — накладывается как есть. "
        "scripts/render_element_overlay.py делает это сам.")
    index_path.write_text(json.dumps(existing, ensure_ascii=False, indent=2), encoding="utf-8")

    emit({
        "status": "ok" if kept else "nothing_kept",
        "downloaded": len(kept) + len(rejected),
        "kept": len(kept),
        "rejected": len(rejected),
        "by_mode": {mode: sum(1 for k in kept if k["mode"] == mode)
                    for mode in ("black", "green", "alpha")},
        "elements": kept,
        "rejected_examples": rejected[:6],
        "library": str(out_root),
        "index": str(index_path),
        "note": ("Отсев большой и это нормально: обычные стоки — библиотеки съёмки, "
                 "элементы там тонут в выдаче. Именно поэтому фильтрует машина."),
    })


if __name__ == "__main__":
    main()
