"use client";

import React, { useCallback, useEffect, useState } from "react";
import Image from "next/image";
import styles from "@/components/administrator/form/form.module.css";
import useUploadFile from "@/hooks/useUploadFile";
import { useDispatch, useSelector } from "react-redux";
import {
  clearResult,
  selectResult,
  selectUploading,
} from "@/utils/store/features/data/upload.slice";
import {
  formatBytes,
  formatDate,
  getImageDimension,
  getImageSize,
  isEmptyObject,
  modifyByKeys,
  objecthasKey,
  spliceObject,
} from "@/app/library/library";

// icons
import { IoCloseOutline, IoReload } from "react-icons/io5";
import InputFileHidden from "../InputFileHidden/input.file.hidden";
import {
  addMiniWindow,
  removeAllMiniWindow,
} from "@/utils/store/features/ui/miniwindow.slice";
import Gallery, {
  ItemGalleryEdit,
} from "@/components/administrator/Gallery/gallery";
import useGalleryManagement from "@/hooks/useGalleryManagement";
import Loader from "@/components/web/Loader/loader";

/**
 *
 * @type {React.FC <{
 *    callback: Function;
 *    onCancel?: Function;
 *    label?: string;
 *    uid?: string;
 *    mode?: "horizontal" | "vertical";
 *    currentImage?: string;
 *    getIsUploaded?: Function
 * }>}
 * @returns
 */
const Uploadbox = ({
  callback,
  onCancel,
  label = "Upload Image",
  uid = null,
  mode = "horizontal",
  currentImage = null,
  getIsUploaded = null,
}) => {
  const dispatch = useDispatch();
  const [image, setImage] = useState(null);
  const [isDragging, setIsDragging] = useState(false);
  const [status, setStatus] = useState({
    error: false,
    success: false,
  });
  const [isLoading, setIsLoading] = useState({
    selectExists: false,
  });
  const uploading = useSelector(selectUploading);
  const uploaded = useSelector(selectResult);

  const { gallery } = useGalleryManagement();
  const { addFile, retry } = useUploadFile({ url: "/api/upload" });

  const handleDragOver = (e) => {
    e.preventDefault();
    if (!isDragging) setIsDragging(true);
  };

  const handleDragLeave = (e) => {
    e.preventDefault();
    if (isDragging) setIsDragging(false);
  };

  const handleDrop = (e) => {
    e.preventDefault();
    if (isDragging) setIsDragging(false);

    const files = Array.from(e.dataTransfer.files);
    if (files.length > 1) files.splice(1); // only apply only 1 file

    addFile(
      files.map((file) => {
        file.uid = uid;
        return file;
      })
    );
    setStatus({ error: false, success: false });
    setImage(null);

    if (getIsUploaded && typeof getIsUploaded === "function")
      getIsUploaded(true);
  };

  const handleSelected = useCallback(
    async (result) => {
      const dimension = await getImageDimension(result.url);
      const detail = await getImageSize(result.url);

      setImage({
        dimension,
        size: detail.bytes,
        type: detail.type,
        ...result,
      });
      if (callback && typeof callback === "function")
        callback(
          {
            dimension,
            size: detail.bytes,
            type: detail.type,
            ...result,
          },
          uid
        );

      dispatch(removeAllMiniWindow());

      if (getIsUploaded && typeof getIsUploaded === "function")
        getIsUploaded(false);
    },
    [callback, dispatch, getIsUploaded, uid]
  );

  const handleCancel = () => {
    setImage(null);
    if (onCancel && typeof onCancel === "function") onCancel();

    if (getIsUploaded && typeof getIsUploaded === "function")
      getIsUploaded(false);
  };

  const handleFromGallery = useCallback(async () => {
    setIsLoading((prev) => ({ ...prev, selectExists: true }));
    await gallery({
      page: 1,
      limit: 50,
      onSuccess: (galleries) => {
        dispatch(
          addMiniWindow({
            status: true,
            component: Gallery,
            initialData: galleries,
            onSelected: async (result) => {
              await handleSelected(result);
            },
          })
        );
      },
      onLoaded: () => {
        setIsLoading((prev) => ({
          ...prev,
          selectExists: false,
        }));
      },
    });
  }, [dispatch, gallery, handleSelected]);

  useEffect(() => {
    if (!currentImage || isEmptyObject(currentImage)) {
      setImage(null);
      return;
    }

    const imageData = async () => {
      const dimension = await getImageDimension(currentImage.url);
      const detail = await getImageSize(currentImage.url);

      setImage({
        dimension,
        size: detail?.bytes || 0,
        type: detail?.type || "",
        ...currentImage,
      });
    };

    // when not onUploading
    if (!currentImage?.raw) imageData();

    setStatus({
      error: currentImage.status === "failed",
      success: currentImage.status === "success",
    });
  }, [currentImage]);

  useEffect(() => {
    if (!uid) return;

    // update progress
    if (uploading.length > 0) {
      const current = uploading.find((item) => item?.uid === uid);
      if (current?.uid === uid) setImage(current);
    }
  }, [uploading, uid]);

  useEffect(() => {
    if (!uid) return;

    if (uploaded.success.length > 0) {
      const current = uploaded.success.find((item) => item?.uid === uid);

      if (current?.uid !== uid) return;
      if (callback && typeof callback === "function")
        callback({ ...current, status: "success" }, uid);

      setImage(current);
      setStatus({ error: false, success: true });

      // clean up
      dispatch(clearResult());

      return;
    } else if (uploaded.failed.length > 0) {
      const current = uploaded.failed.find((item) => item?.uid === uid);

      if (!current) return;

      setStatus({ error: true, success: false });
      if (callback && typeof callback === "function")
        callback({ ...current, status: "failed" }, uid);

      return;
    }
  }, [uploaded, callback, uid, dispatch]);

  return (
    <>
      <div className={styles.upload_container}>
        <div className={styles.uploadbox_title}>{label}</div>
        <div
          className={`${styles.upload_session} ${
            mode === "vertical" && styles.vertical
          }`}
          onDragLeave={handleDragLeave}
          onDragOver={handleDragOver}
          onDrop={handleDrop}>
          {!image && (
            <div
              className={`${styles.upload_action} ${
                mode === "vertical" && styles.vertical
              } ${isDragging && styles.drag}`}>
              <div
                className={`${styles.action_upload} ${
                  mode === "vertical" && styles.vertical
                }`}>
                <InputFileHidden
                  onChange={(files, e) => {
                    if (files.length > 1) files.splice(1); // only apply only 1 file

                    // set file and add uid for prevent duplicate
                    addFile(
                      files.map((file) => {
                        file.uid = uid;
                        return file;
                      })
                    );
                    setStatus({ error: false, success: false });
                    setImage(null);

                    if (getIsUploaded && typeof getIsUploaded === "function")
                      getIsUploaded(true);
                  }}>
                  {(_, onClick) => {
                    return (
                      <button onClick={() => onClick()}>Choose local</button>
                    );
                  }}
                </InputFileHidden>
                or
                {/* Not implemented yet, for gallery */}
                <button
                  disabled={isLoading.selectExists}
                  onClick={handleFromGallery}>
                  {isLoading.selectExists && (
                    <Loader
                      width={13}
                      color={"#000"}
                    />
                  )}
                  Choose from existing
                </button>
              </div>
              {mode !== "vertical" && (
                <div className={styles.action_title}>Drag and Drop here</div>
              )}
            </div>
          )}

          {!!image && (
            <>
              <div
                className={`${styles.upload_image} ${
                  mode === "vertical" && styles.vertical
                }`}>
                <div
                  className={`${styles.image_container} ${
                    mode === "vertical" && styles.vertical
                  }`}>
                  <Image
                    src={image?.url}
                    alt={"image"}
                    width={300}
                    height={300}
                    className={styles.image}
                  />
                </div>
                <div className={styles.image_detail}>
                  <div className={styles.detail}>
                    <p>
                      <span
                        onClick={() => {
                          dispatch(
                            addMiniWindow({
                              status: true,
                              component: ItemGalleryEdit,
                              data: modifyByKeys(
                                objecthasKey(image, "files")
                                  ? image.files?.[0]
                                  : spliceObject(image, [
                                      "dimension",
                                      "size",
                                      "type",
                                    ]).spliced,
                                ["createdAt"],
                                (date) => formatDate(date, true)
                              ),
                              onUpdate: async (result) => {
                                await handleSelected(result);
                              },
                              onDelete: (result) => {
                                handleCancel();
                              },
                              height: "fit-content",
                            })
                          );
                        }}>
                        {image?.filename}
                      </span>
                    </p>
                    <p>
                      size:{" "}
                      <span>
                        {formatBytes(image?.raw?.size || image?.size)}{" "}
                      </span>
                      — dimension:{" "}
                      <span>{`${image?.dimension?.width} x ${image?.dimension?.height}`}</span>{" "}
                      — type: <span>{image?.raw?.type || image?.type}</span>
                    </p>
                    {image.raw && (
                      <>
                        {!status.error && !status.success && (
                          <p>
                            progress: <span>{image?.progress}%</span>
                          </p>
                        )}
                        {Object.values(status).some((x) => x) && (
                          <p>
                            status:{" "}
                            <span>
                              {status.error && "❌ Error"}{" "}
                              {status.success && "✅ Uploaded"}
                            </span>
                          </p>
                        )}
                      </>
                    )}
                  </div>
                  <div
                    className={`${styles.action_upload_button} ${
                      mode === "vertical" && styles.vertical
                    }`}>
                    {status.error && (
                      <IoReload
                        className={styles.icon}
                        onClick={() => {
                          setStatus({ error: false, success: false });
                          retry(image);
                        }}
                      />
                    )}
                    <IoCloseOutline
                      className={styles.icon}
                      onClick={() => {
                        handleCancel();
                      }}
                    />
                  </div>
                </div>
              </div>
            </>
          )}
        </div>
      </div>
    </>
  );
};

export default Uploadbox;
