#!/usr/bin/env python3
"""
Minimal cross-platform motion detector -> preview_agent.py's motion webhook.

RoboVisionAI_PI's real app_pi_clean.py does full object detection + motion
+ MJPEG streaming in one Flask app, but its motion loop only actually runs
while something is actively pulling /video_feed, and it wants Pi-specific
caffemodel files for the object-detection half. For a plain "does motion
actually reach the conversation pipeline" test (or a lightweight always-on
trigger on a robot that doesn't need the vision overlay), this is a much
smaller piece: open whatever camera OpenCV can see, do frame-differencing
motion detection, POST to the webhook on motion, done. No Flask, no model
files, works the same on Windows/Linux/Pi wherever cv2 can open a camera.

preview_agent.py's webhook handler accepts any POST body (even {}) as a
trigger signal -- it does not require the RoboVisionAI_PI payload shape --
so this only needs to hit the URL, not match any particular JSON schema.

Usage:
    python vision_motion_trigger.py --webhook-url http://localhost:5058/
    python vision_motion_trigger.py --camera 0 --min-area 800 --cooldown 15
"""
from __future__ import annotations

import argparse
import logging
import time

logger = logging.getLogger("robopark.vision_motion_trigger")


def run(camera_index: int, webhook_url: str, min_area: float, cooldown: float, fps_limit: float) -> None:
    import cv2
    import httpx

    cap = cv2.VideoCapture(camera_index)
    if not cap.isOpened():
        raise SystemExit(f"could not open camera index {camera_index}")
    logger.info(f"camera {camera_index} opened, watching for motion (min_area={min_area}px, cooldown={cooldown}s)")

    prev_gray = None
    last_trigger = 0.0
    frame_interval = 1.0 / fps_limit if fps_limit > 0 else 0.0
    client = httpx.Client(timeout=5.0)

    try:
        while True:
            loop_start = time.monotonic()
            ok, frame = cap.read()
            if not ok:
                logger.warning("camera read failed, retrying")
                time.sleep(1.0)
                continue

            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            gray = cv2.GaussianBlur(gray, (21, 21), 0)

            if prev_gray is not None:
                delta = cv2.absdiff(prev_gray, gray)
                thresh = cv2.threshold(delta, 25, 255, cv2.THRESH_BINARY)[1]
                thresh = cv2.dilate(thresh, None, iterations=2)
                contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
                motion = any(cv2.contourArea(c) >= min_area for c in contours)

                if motion:
                    now = time.time()
                    if now - last_trigger >= cooldown:
                        last_trigger = now
                        logger.info("motion detected, posting to webhook")
                        try:
                            client.post(webhook_url, json={"source": "vision_motion_trigger"})
                        except Exception as e:
                            logger.warning(f"webhook POST failed: {e}")
                    else:
                        logger.debug("motion detected, still in cooldown")

            prev_gray = gray

            if frame_interval:
                elapsed = time.monotonic() - loop_start
                if elapsed < frame_interval:
                    time.sleep(frame_interval - elapsed)
    finally:
        cap.release()
        client.close()


def main() -> None:
    logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s: %(message)s")
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--camera", type=int, default=0, help="OpenCV camera index (default 0)")
    parser.add_argument("--webhook-url", default="http://localhost:5058/", help="preview_agent.py's motion webhook")
    parser.add_argument("--min-area", type=float, default=500.0, help="minimum changed-pixel contour area to count as motion")
    parser.add_argument("--cooldown", type=float, default=20.0, help="minimum seconds between triggers")
    parser.add_argument("--fps-limit", type=float, default=5.0, help="cap capture rate to reduce CPU (0 = uncapped)")
    args = parser.parse_args()
    run(args.camera, args.webhook_url, args.min_area, args.cooldown, args.fps_limit)


if __name__ == "__main__":
    main()
