import { toast } from "@/hooks/use-toast";
import { uploadImage } from "@/lib/api/writer-tools.v2";
import { getErrorMessage } from "@/lib/errors";
import byteSize from "byte-size";
import {
  type ClipboardEvent,
  type Dispatch,
  type DragEvent,
  type RefObject,
  type SetStateAction,
  useEffect,
  useRef,
  useState,
} from "react";

export function useUploadImages(setContent: Dispatch<SetStateAction<string>>) {
  const mounted = useRef(false);
  const [draggingOver, setDraggingOver] = useState(false);

  useEffect(() => {
    mounted.current = true;
    return () => {
      mounted.current = false;
    };
  }, []);

  const handlePaste = (e: ClipboardEvent<HTMLTextAreaElement>) => {
    const images = collectImages(e.clipboardData.files);
    uploadImages(
      e,
      images,
      mounted,
      setContent,
      e.clipboardData.files.length > 0
    );
  };

  const handleDragOver = (e: DragEvent<HTMLTextAreaElement>) => {
    e.preventDefault();
  };

  const handleDragStart = (e: DragEvent<HTMLTextAreaElement>) => {
    setDraggingOver(e.dataTransfer.types.includes("Files"));
  };

  const handleDragStop = () => {
    setDraggingOver(false);
  };

  const handleDrop = (e: DragEvent<HTMLTextAreaElement>) => {
    const images = collectImages(e.dataTransfer.files);
    if (e.dataTransfer.types.includes("Files")) {
      setDraggingOver(false);
    }
    uploadImages(
      e,
      images,
      mounted,
      setContent,
      e.dataTransfer.types.includes("Files")
    );
  };

  return {
    handlePaste,
    handleDragOver,
    handleDragStart,
    handleDragStop,
    handleDrop,
    draggingOver: draggingOver,
  };
}

function collectImages(files: FileList) {
  const images: File[] = [];
  for (let i = 0; i < files.length; i++) {
    const file = files.item(i)!;
    if (/^image\/*/.test(file.type)) {
      images.push(file);
    } else {
      toast({
        title: `File ${file.name} is not supported to upload currently.`,
      });
    }
  }

  return images;
}

function uploadImages(
  e: ClipboardEvent<HTMLTextAreaElement> | DragEvent<HTMLTextAreaElement>,
  images: File[],
  mounted: RefObject<boolean>,
  setContent: Dispatch<SetStateAction<string>>,
  hasAnyFile: boolean
) {
  let fragments = "";
  const now = Date.now();

  const { selectionStart, selectionEnd } = e.currentTarget;

  if (hasAnyFile) {
    e.preventDefault();
  }

  if (images.length > 0) {
    images.forEach((image, index) => {
      if (image.size > 300 * 1024) {
        toast({
          title: `Failed to upload image ${image.name}`,
          description: (
            <>
              Image size exceeds{" "}
              {byteSize(300 * 1024, { units: "iec" }).toString()}. Use{" "}
              <a
                className="underline"
                href="https://tinypng.com/"
                target="_blank"
              >
                https://tinypng.com/
              </a>{" "}
              to compress your image.
            </>
          ),
          variant: "destructive",
        });
        return;
      }

      const tempFragment = `![Uploading...](${now}-${index}-${image.name})`;
      fragments += `${index > 0 ? " " : ""}${tempFragment}`;

      const loader = toast({
        title: `Uploading image ${image.name}...`,
      });

      uploadImage(image)
        .then((res) => {
          if (mounted.current) {
            loader.update({
              id: loader.id,
              title: `Image ${image.name} successfully uploaded!`,
            });
            setContent((content) =>
              content.replaceAll(
                tempFragment,
                `![${image.name} 85%](${res.public_url})`
              )
            );
          }
        })
        .catch((e) => {
          toast({
            title: `Failed to upload image ${image.name}`,
            description: getErrorMessage(e),
            variant: "destructive",
          });
        });
    });
    if (e.type === "drop") {
      setContent((content) => content + "\n" + fragments);
      const el = e.currentTarget;
      setTimeout(() => {
        el.selectionStart = el.value.length - fragments.length;
        el.selectionEnd = el.value.length;
        el.scrollTo({ top: el.scrollHeight, behavior: "smooth" });
        el.focus();
      }, 100);
    } else {
      setContent(
        (content) =>
          content.slice(0, selectionStart) +
          fragments +
          content.slice(selectionEnd)
      );
    }
  }
}
