import { useEffect, useRef, useState } from 'react';
import { traceImage, simplifyVector } from '../api/engine';
import type { TraceMetadata } from '../types/engine';

interface UseDebouncedTraceResult {
  svgOutput: string | null;
  metadata: TraceMetadata | null;
  isLoading: boolean;
  error: string | null;
}

function isSvgFile(file: File): boolean {
  return (
    file.type === 'image/svg+xml' ||
    file.name.toLowerCase().endsWith('.svg')
  );
}

/**
 * Custom hook that debounces vectorization requests with AbortController.
 *
 * On each parameter/file/preset change, it clears any pending timeout,
 * aborts any in-flight request, and schedules a new trace after `debounceMs`.
 * SVG file uploads route to simplifyVector instead of traceImage.
 */
export function useDebouncedTrace(
  file: File | null,
  preset: string,
  params: Record<string, unknown>,
  debounceMs: number = 300,
): UseDebouncedTraceResult {
  const [svgOutput, setSvgOutput] = useState<string | null>(null);
  const [metadata, setMetadata] = useState<TraceMetadata | null>(null);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const abortRef = useRef<AbortController | null>(null);
  const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // Stabilize params by value to prevent effect re-runs on same-value new objects
  const paramsKey = JSON.stringify(params);

  useEffect(() => {
    // Clear any pending debounce timeout
    if (timeoutRef.current !== null) {
      clearTimeout(timeoutRef.current);
      timeoutRef.current = null;
    }

    // Abort any in-flight request
    if (abortRef.current) {
      abortRef.current.abort();
      abortRef.current = null;
    }

    if (!file) {
      setSvgOutput(null);
      setMetadata(null);
      setIsLoading(false);
      setError(null);
      return;
    }

    // Parse the stabilized params key back to object
    const currentParams: Record<string, unknown> = JSON.parse(paramsKey);

    setIsLoading(true);
    setError(null);

    timeoutRef.current = setTimeout(async () => {
      const controller = new AbortController();
      abortRef.current = controller;

      try {
        let response;
        if (isSvgFile(file)) {
          const epsilon =
            typeof currentParams.epsilon === 'number' ? currentParams.epsilon : 2.5;
          response = await simplifyVector(file, epsilon, controller.signal);
        } else {
          response = await traceImage(
            file,
            preset,
            currentParams,
            controller.signal,
          );
        }

        // Only update state if this request wasn't aborted
        if (!controller.signal.aborted) {
          setSvgOutput(response.output);
          setMetadata(response.metadata);
          setIsLoading(false);
        }
      } catch (err) {
        // Ignore abort errors — they're expected during cancellation
        if (err instanceof Error && err.name === 'AbortError') {
          return;
        }
        if (!controller.signal.aborted) {
          setError(
            err instanceof Error ? err.message : 'Vectorization failed',
          );
          setIsLoading(false);
        }
      }
    }, debounceMs);

    return () => {
      if (timeoutRef.current !== null) {
        clearTimeout(timeoutRef.current);
        timeoutRef.current = null;
      }
      if (abortRef.current) {
        abortRef.current.abort();
        abortRef.current = null;
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [file, preset, paramsKey, debounceMs]);

  return { svgOutput, metadata, isLoading, error };
}
