import { useState, useCallback, useEffect, useRef } from "react";
import { convertToWebP, ConvertToWebPOptions } from "../helpers/webPConverter";

export interface UseWebPUploadReturn {
  upload: File | undefined;
  img: string | undefined;
  error: Error | undefined;
  loading: boolean;
  handleFileUpload: (file: File) => Promise<void>;
  reset: () => void;
}

/**
 * Hook to allow the inline converting any image to webp, before sending the
 * converted file up to the backend, rather than the (usually much larger)
 * original. Intended to be used with a `file upload` form element.
 */
export function useWebPImageUploader(
  options: ConvertToWebPOptions = {},
): UseWebPUploadReturn {
  const { maxWidth = 1200, maxHeight, quality } = options;

  const [upload, setUpload] = useState<File | undefined>(undefined);
  const [img, setImg] = useState<string | undefined>(undefined);
  const [error, setError] = useState<Error | undefined>(undefined);
  const [loading, setLoading] = useState<boolean>(false);

  // Track current object URL to revoke immediately when replaced
  const objectUrlRef = useRef<string | undefined>(undefined);

  const handleFileUpload = useCallback(
    async (file: File): Promise<void> => {
      setLoading(true);
      setError(undefined);

      try {
        const webPBlob = await convertToWebP(file, {
          maxWidth,
          maxHeight,
          quality,
        });

        const baseName = file.name.includes(".")
          ? file.name.substring(0, file.name.lastIndexOf("."))
          : file.name;

        const newName = `${baseName}.webp`;

        const webPFile = new File([webPBlob], newName, {
          type: "image/webp",
        });

        // Revoke previous URL before replacing
        if (objectUrlRef.current) {
          URL.revokeObjectURL(objectUrlRef.current);
        }

        const objectUrl = URL.createObjectURL(file);
        objectUrlRef.current = objectUrl;

        setUpload(webPFile);
        setImg(objectUrl);
      } catch (err: unknown) {
        if (err instanceof Error) {
          setError(err);
        } else {
          setError(new Error("Unknown error occurred"));
        }
        console.error(err);
      } finally {
        setLoading(false);
      }
    },
    [maxWidth, maxHeight, quality],
  );

  const reset = useCallback(() => {
    if (objectUrlRef.current) {
      URL.revokeObjectURL(objectUrlRef.current);
      objectUrlRef.current = undefined;
    }

    setUpload(undefined);
    setImg(undefined);
    setError(undefined);
    setLoading(false);
  }, []);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (objectUrlRef.current) {
        URL.revokeObjectURL(objectUrlRef.current);
      }
    };
  }, []);

  return {
    upload,
    img,
    error,
    loading,
    handleFileUpload,
    reset,
  };
}