import { useCallback, useEffect, useRef, useState } from 'react';
import { encodeImage } from '@/lib/image-upload/encodeImage';
import { uploadEncoded } from '@/lib/image-upload/uploadImage';
import { PURPOSES, type ImagePurpose } from '@/lib/imageVariants';

export type UploadPhase =
  | 'idle'
  | 'hashing'
  | 'decoding'
  | 'encoding'
  | 'uploading'
  | 'done'
  | 'error'
  | 'cancelled';

export interface UseImageUploadResult {
  phase: UploadPhase;
  encodingProgress: number; // 0–1, only meaningful in 'encoding' phase
  sha256: string | null; // set on 'done'
  url: string | null; // set on 'done' — serving URL for the uploaded original
  r2Key: string | null; // set on 'done' — R2 object key
  error: string | null; // set on 'error'
  startUpload: (file: File) => void;
  cancel: () => void;
  reset: () => void;
}

export function useImageUpload(
  purpose: ImagePurpose,
  onComplete?: (sha256: string) => void,
): UseImageUploadResult {
  const [phase, setPhase] = useState<UploadPhase>('idle');
  const [encodingProgress, setEncodingProgress] = useState(0);
  const [sha256, setSha256] = useState<string | null>(null);
  const [url, setUrl] = useState<string | null>(null);
  const [r2Key, setR2Key] = useState<string | null>(null);
  const [error, setError] = useState<string | null>(null);

  // Wrap onComplete in a ref so startUpload closure doesn't go stale
  const onCompleteRef = useRef(onComplete);
  useEffect(() => {
    onCompleteRef.current = onComplete;
  }, [onComplete]);

  const abortControllerRef = useRef<AbortController | null>(null);

  const startUpload = useCallback(
    (file: File) => {
      // No-op if an upload is already in progress
      if (phase !== 'idle' && phase !== 'cancelled' && phase !== 'done' && phase !== 'error')
        return;

      const ac = new AbortController();
      abortControllerRef.current = ac;

      setSha256(null);
      setUrl(null);
      setR2Key(null);
      setError(null);
      setEncodingProgress(0);
      setPhase('hashing');

      (async () => {
        try {
          const bundle = await encodeImage(
            file,
            (progress) => {
              if (progress.phase === 'encoding') {
                setPhase('encoding');
                const current = progress.variantIndex ?? 0;
                const total = progress.variantTotal ?? 1;
                setEncodingProgress(current / total);
              } else if (progress.phase === 'decoding') {
                setPhase('decoding');
              }
              // 'hashing' phase is already set before the call
            },
            ac.signal,
            PURPOSES[purpose].variants,
          );

          if (ac.signal.aborted) return;

          setPhase('uploading');
          const result = await uploadEncoded(bundle, purpose);

          if (ac.signal.aborted) return;

          setSha256(result.sha256);
          setUrl(result.url);
          setR2Key(result.r2Key);
          setPhase('done');
          onCompleteRef.current?.(result.sha256);
        } catch (err) {
          if (ac.signal.aborted) {
            setPhase('cancelled');
            return;
          }
          const message = err instanceof Error ? err.message : String(err);
          setError(message);
          setPhase('error');
        }
      })();
    },
    [phase, purpose],
  );

  const cancel = useCallback(() => {
    abortControllerRef.current?.abort();
    setPhase('cancelled');
  }, []);

  const reset = useCallback(() => {
    abortControllerRef.current = null;
    setSha256(null);
    setUrl(null);
    setR2Key(null);
    setError(null);
    setEncodingProgress(0);
    setPhase('idle');
  }, []);

  return {
    phase,
    encodingProgress,
    sha256,
    url,
    r2Key,
    error,
    startUpload,
    cancel,
    reset,
  };
}
