"""CLI argparse + JSON Schema validation + progress reporting."""

from __future__ import annotations

import argparse
import json
import sys
from typing import Callable

from loguru import logger


REMOVE_VIDEO_OBJECT_SCHEMA: dict = {
    "name": "remove_video_object",
    "description": "Remove a specified object from a video using AI inpainting",
    "parameters": {
        "type": "object",
        "properties": {
            "video_path": {
                "type": "string",
                "description": "Path to the input video file",
            },
            "target": {
                "type": "string",
                "description": "Text description of the object to remove",
            },
            "output_path": {
                "type": "string",
                "description": "Path for the output video",
            },
            "box": {
                "type": "array",
                "items": {"type": "integer"},
                "minItems": 4,
                "maxItems": 4,
                "description": "Bounding box [x1, y1, x2, y2] for initial frame",
            },
            "points": {
                "type": "array",
                "items": {"type": "array", "items": {"type": "integer"}, "minItems": 2, "maxItems": 2},
                "description": "Point prompts [[x, y], ...]",
            },
            "sensitivity": {
                "type": "number",
                "minimum": 0.1,
                "maximum": 1.0,
                "default": 0.5,
                "description": "Mask dilation sensitivity (0.1=tight, 1.0=aggressive)",
            },
            "sam_model_size": {
                "type": "string",
                "enum": ["tiny", "small", "base_plus", "large"],
                "default": "large",
            },
            "output_format": {
                "type": "string",
                "enum": ["mp4", "mkv"],
                "default": "mp4",
            },
        },
        "required": ["video_path", "target"],
    },
}


class ProgressReporter:
    """Report progress at 5% intervals."""

    def __init__(self, callback: Callable[[float, str, str], None] | None = None) -> None:
        self._callback = callback or self._default_callback
        self._last_reported: float = -5.0

    def report(self, percent: float, stage: str, detail: str = "") -> None:
        if percent - self._last_reported >= 5.0 or percent >= 100.0:
            self._last_reported = percent
            self._callback(percent, stage, detail)

    @staticmethod
    def _default_callback(percent: float, stage: str, detail: str) -> None:
        print(f"[{percent:5.1f}%] {stage}: {detail}")


class SkillInterface:
    def parse_args(self, argv: list[str] | None = None) -> dict:
        """Parse CLI arguments into config dict."""
        parser = argparse.ArgumentParser(
            prog="ghost-remover",
            description="AI Video Inpainting - remove watermarks and objects",
        )
        sub = parser.add_subparsers(dest="command")

        remove = sub.add_parser("remove", help="Remove object from video")
        remove.add_argument("--video", required=True, nargs="+", help="Input video path(s)")
        remove.add_argument("--target", required=True, help="Text description of object to remove")
        remove.add_argument("--output", default=None, help="Output video path (single video only)")
        remove.add_argument("--box", default=None, help="Bounding box x1,y1,x2,y2")
        remove.add_argument("--points", default=None, help="Point prompts x1,y1;x2,y2;...")
        remove.add_argument("--sensitivity", type=float, default=0.5, help="Mask sensitivity 0.1-1.0")
        remove.add_argument("--sam-model", default="large", choices=["tiny", "small", "base_plus", "large"])
        remove.add_argument("--format", default="mp4", choices=["mp4", "mkv"])
        remove.add_argument("--dry-run", action="store_true", help="Preview masks only, skip inpainting")
        remove.add_argument("--hw-accel", action="store_true", help="Use VideoToolbox hardware encoding")
        remove.add_argument("--verbose", action="store_true")

        args = parser.parse_args(argv)

        if args.command != "remove":
            parser.print_help()
            sys.exit(1)

        # Build base config (shared across batch)
        base: dict = {
            "target": args.target,
            "sensitivity": max(0.1, min(1.0, args.sensitivity)),
            "sam_model_size": args.sam_model,
            "output_format": args.format,
        }

        if args.output:
            base["output_path"] = args.output
        if args.dry_run:
            base["dry_run"] = True
        if args.hw_accel:
            base["hw_accel"] = True
        if args.verbose:
            base["verbose"] = True

        if args.box:
            try:
                parts = [int(x.strip()) for x in args.box.split(",")]
                if len(parts) == 4:
                    base["box"] = tuple(parts)
            except ValueError:
                logger.warning(f"Invalid --box format: {args.box}, ignoring")

        if args.points:
            try:
                pairs = args.points.split(";")
                base["points"] = [tuple(int(x) for x in p.split(",")) for p in pairs if "," in p]
            except ValueError:
                logger.warning(f"Invalid --points format: {args.points}, ignoring")

        # Batch support: multiple --video paths
        videos = args.video if isinstance(args.video, list) else [args.video]
        if len(videos) == 1:
            base["video_path"] = videos[0]
            return base

        # Return list of configs for batch mode
        base["_batch"] = True
        base["_video_paths"] = videos
        base["video_path"] = videos[0]  # default to first for compat
        return base

    def parse_json(self, payload: dict) -> dict:
        """Validate and normalize JSON request against schema."""
        props = REMOVE_VIDEO_OBJECT_SCHEMA["parameters"]["properties"]
        required = REMOVE_VIDEO_OBJECT_SCHEMA["parameters"]["required"]

        # Check required fields
        for field in required:
            if field not in payload:
                raise ValueError(f"Missing required field: {field}")

        config: dict = {
            "video_path": str(payload["video_path"]),
            "target": str(payload["target"]),
        }

        if "output_path" in payload:
            config["output_path"] = str(payload["output_path"])

        if "box" in payload:
            box = payload["box"]
            if isinstance(box, (list, tuple)) and len(box) == 4:
                config["box"] = tuple(int(x) for x in box)

        if "points" in payload:
            config["points"] = [tuple(int(x) for x in p) for p in payload["points"]]

        # Sensitivity: clamp to valid range
        sens = float(payload.get("sensitivity", 0.5))
        config["sensitivity"] = max(0.1, min(1.0, sens))

        config["sam_model_size"] = payload.get("sam_model_size", "large")
        if config["sam_model_size"] not in ("tiny", "small", "base_plus", "large"):
            config["sam_model_size"] = "large"

        config["output_format"] = payload.get("output_format", "mp4")
        if config["output_format"] not in ("mp4", "mkv"):
            config["output_format"] = "mp4"

        return config


def main() -> None:
    """CLI entry point with batch support (B3)."""
    interface = SkillInterface()
    config = interface.parse_args()

    if config.get("verbose"):
        logger.enable("")
    else:
        logger.disable("")

    from main import GhostRemoverPipeline

    reporter = ProgressReporter()

    # Batch mode: process multiple videos
    video_paths = config.pop("_video_paths", None) or [config["video_path"]]
    config.pop("_batch", None)

    for idx, vpath in enumerate(video_paths):
        if len(video_paths) > 1:
            print(f"\n=== [{idx+1}/{len(video_paths)}] {vpath} ===")

        run_config = {**config, "video_path": vpath}
        # Remove single output_path for batch (auto-generate per video)
        if len(video_paths) > 1:
            run_config.pop("output_path", None)

        pipeline = GhostRemoverPipeline(run_config, progress_callback=reporter.report)
        try:
            output = pipeline.run()
            print(f"Done: {output}")
        except KeyboardInterrupt:
            print("\nInterrupted. Progress saved — resume by running the same command.")
            break
        except Exception as e:
            logger.error(f"Pipeline failed for {vpath}: {e}")
            if len(video_paths) == 1:
                sys.exit(1)
        finally:
            pipeline.cleanup()


if __name__ == "__main__":
    main()
