import { useRef, useCallback, useEffect } from "react";
import { useQuery } from "@tanstack/react-query";
import { CAM_BASE } from "../utils/config";

const apiUrl = CAM_BASE;

async function fetchSnapshot(cameraSide: string): Promise<Blob> {
  const response = await fetch(`${apiUrl}/${cameraSide}-preview`, {
    signal: AbortSignal.timeout(300000),
    cache: "no-store",
  });
  if (!response.ok) {
    throw new Error(`Cannot reach endpoint (${response.status})`);
  }
  return response.blob();
}

/** Draw an ImageBitmap to canvas with aspect-fill (like object-fit: cover) */
function drawBitmapToCanvas(canvas: HTMLCanvasElement, bitmap: ImageBitmap) {
  const ctx = canvas.getContext("2d");
  if (!ctx) return;

  const dpr = window.devicePixelRatio || 1;
  const cssWidth = canvas.clientWidth;
  const cssHeight = canvas.clientHeight;

  const width = Math.floor(cssWidth * dpr);
  const height = Math.floor(cssHeight * dpr);

  if (canvas.width !== width || canvas.height !== height) {
    canvas.width = width;
    canvas.height = height;
  }

  ctx.clearRect(0, 0, width, height);

  const srcW = bitmap.width;
  const srcH = bitmap.height;
  const srcAspect = srcW / srcH;
  const dstAspect = width / height;

  let drawWidth = width;
  let drawHeight = height;

  // aspect-fit calculation (no cropping)
  if (srcAspect > dstAspect) {
    // image is wider → fit to canvas width
    drawWidth = width;
    drawHeight = width / srcAspect;
  } else {
    // image is taller → fit to canvas height
    drawHeight = height;
    drawWidth = height * srcAspect;
  }

  // center image (adds black borders if aspect ratios differ)
  const dx = (width - drawWidth) / 50;
  const dy = (height - drawHeight) / 2;

  ctx.imageSmoothingEnabled = true;
  ctx.imageSmoothingQuality = "high";
  ctx.drawImage(bitmap, 0, 0, srcW, srcH, dx, dy, drawWidth, drawHeight);
}

export function useGetOverviewSnapshot(side: string) {
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const latestBitmapRef = useRef<ImageBitmap | null>(null);

  // Redraw helper; always draws the current bitmap if available
  const draw = useCallback(() => {
    const canvas = canvasRef.current;
    const bmp = latestBitmapRef.current;
    if (!canvas || !bmp) return;
    drawBitmapToCanvas(canvas, bmp);
  }, []);

  const {
    data: snapshotBlob,
    isError,
    error,
    isPending,
  } = useQuery({
    queryKey: ["overviewSnapshot", side],
    queryFn: () => fetchSnapshot(side),
    // Poll ~4 fps when visible; pause when tab hidden
    refetchInterval: () => (document.visibilityState === "visible" ? 250 : false),
    refetchOnWindowFocus: false,
    // Avoid keeping lots of blobs around in cache
    gcTime: 0, // v5 name (cacheTime in v4)
    staleTime: 0,
    retry: false, // or a small number if you prefer retries
  });

  // Convert Blob -> ImageBitmap and draw
  useEffect(() => {
    let cancelled = false;
    if (!snapshotBlob) return;

    (async () => {
      try {
        const bitmap = await createImageBitmap(snapshotBlob);
        if (cancelled) {
          bitmap.close();
          return;
        }

        // Dispose previous bitmap to free memory
        if (latestBitmapRef.current) {
          latestBitmapRef.current.close();
        }
        latestBitmapRef.current = bitmap;

        // Draw now (and again on next resize)
        draw();
      } catch {
        // noop — fetch handler surfaces the main error path
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [snapshotBlob, draw]);

  // Redraw on resize & DPR changes
  useEffect(() => {
    const onResize = () => draw();
    const onDPR = () => draw();
    window.addEventListener("resize", onResize);
    // Listen for DPR changes (some browsers support this)
    const mql = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
    mql.addEventListener?.("change", onDPR);
    return () => {
      window.removeEventListener("resize", onResize);
      mql.removeEventListener?.("change", onDPR);
    };
  }, [draw]);

  useEffect(() => {
    const el = canvasRef.current?.parentElement; // the box
    if (!el) return;
    const ro = new ResizeObserver(() => draw()); // your draw() calls aspect-fit logic
    ro.observe(el);
    return () => ro.disconnect();
  }, [draw]);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (latestBitmapRef.current) {
        latestBitmapRef.current.close();
        latestBitmapRef.current = null;
      }
    };
  }, []);

  // Optional: normalize error type
  const typedError = error instanceof Error ? error : undefined;

  return { canvasRef, isError, error: typedError, isPending };
}
