#!/usr/bin/env python3
import argparse
import json
import re
from pathlib import Path

import cv2
import pytesseract


def parse_args() -> argparse.Namespace:
    p = argparse.ArgumentParser(description="Compute OCR hit ratio across one or more clips.")
    p.add_argument(
        "--clip",
        action="append",
        required=True,
        help="Clip spec as label=/absolute/or/relative/path.mp4 (repeatable)",
    )
    p.add_argument("--output-json", required=True)
    p.add_argument("--min-conf", type=float, default=62.0)
    p.add_argument("--min-strong-boxes", type=int, default=2)
    return p.parse_args()


def has_text_token(text: str) -> bool:
    return bool(re.search(r"[A-Za-z0-9\u0600-\u06FF]", text or ""))


def parse_clip_spec(spec: str) -> tuple[str, Path]:
    if "=" not in spec:
        raise RuntimeError(f"Invalid --clip format (expected label=path): {spec}")
    label, path = spec.split("=", 1)
    label = label.strip()
    path = Path(path.strip())
    if not label:
        raise RuntimeError(f"Empty label in --clip spec: {spec}")
    return label, path


def main() -> None:
    args = parse_args()
    clips = [parse_clip_spec(s) for s in args.clip]
    results: dict[str, dict[str, float | int]] = {}

    for label, path in clips:
        cap = cv2.VideoCapture(str(path))
        if not cap.isOpened():
            raise RuntimeError(f"Cannot open clip: {path}")
        frames = 0
        hit_frames = 0

        while True:
            ok, frame = cap.read()
            if not ok:
                break
            frames += 1
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            data = pytesseract.image_to_data(
                gray,
                config="--oem 1 --psm 6",
                output_type=pytesseract.Output.DICT,
            )
            strong = 0
            for text, conf_s in zip(data.get("text", []), data.get("conf", [])):
                try:
                    conf = float(conf_s)
                except Exception:
                    conf = -1.0
                if conf >= args.min_conf and has_text_token((text or "").strip()):
                    strong += 1
            if strong >= args.min_strong_boxes:
                hit_frames += 1

        cap.release()
        ratio = (hit_frames / frames) if frames else None
        results[label] = {
            "frames": frames,
            "ocr_hit_frames": hit_frames,
            "ocr_hit_ratio": ratio,
        }

    out_path = Path(args.output_json)
    out_path.parent.mkdir(parents=True, exist_ok=True)
    out_path.write_text(json.dumps(results, indent=2), encoding="utf-8")
    print(json.dumps(results, indent=2))


if __name__ == "__main__":
    main()
