"""
Video utilities for iterating video frames with consistent logging and error handling.
"""

from collections.abc import Callable
from typing import Any

import cv2

from app.core.logging import get_logger

logger = get_logger("video_utils")


def foreach_frame(
    video_path: str,
    handle_frame: Callable[[int, Any], None],
    sample_rate: int = 1,
    progress_every: int = 100,
    frame_limit: int | None = None,
) -> dict[str, Any]:
    """Iterate frames of a video, calling handle_frame(index, frame) on sampled frames.

    Returns stats: { total_frames, processed_frames, fps, errors }.
    """
    cap = cv2.VideoCapture(video_path)
    if not cap.isOpened():
        raise Exception("Failed to open video file")

    fps = cap.get(cv2.CAP_PROP_FPS)
    total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    frame_index = 0
    processed_frames = 0
    errors = 0

    try:
        # Use frame skipping via CAP_PROP_POS_FRAMES when sampling to avoid decoding all frames
        stride = max(sample_rate, 1)
        while True:
            if frame_limit is not None and frame_index >= frame_limit:
                break

            # Seek to the next frame index directly for sampled processing
            if stride > 1 and frame_index > 0:
                cap.set(cv2.CAP_PROP_POS_FRAMES, frame_index)

            ret, frame = cap.read()
            if not ret:
                break

            try:
                handle_frame(frame_index, frame)
                processed_frames += 1
            except Exception as e:
                logger.warning(f"Frame {frame_index} handler failed: {e}")
                errors += 1

            if progress_every and frame_index % progress_every == 0 and frame_index > 0:
                logger.info(f"Processed {frame_index}/{total_frames} frames")

            # advance by stride
            frame_index += stride
    finally:
        cap.release()

    return {
        "total_frames": total_frames,
        "processed_frames": processed_frames,
        "fps": fps,
        "errors": errors,
    }
