"use client";

import React, {
  memo,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";

import styles from "@/components/administrator/Gallery/gallery.module.css";
import Button from "@/components/ui/Button/button";

import Table from "@/components/administrator/Table/table";
import { formatDate, modifyByKeys } from "@/app/library/library";
import { useDispatch, useSelector } from "react-redux";
import { setNotification } from "@/utils/store/features/ui/notification.slice";
import {
  addMiniWindow,
  hideMiniWindowBy,
  hideMiniWindowOnIndex,
} from "@/utils/store/features/ui/miniwindow.slice";
import SafeImage from "../../ui/SafeImage/safeImage";
import Input from "@/components/administrator/form/Input/input";
import useGalleryManagement from "@/hooks/useGalleryManagement";
import { useSearch } from "@/hooks/useSearch";
import Loader from "@/components/web/Loader/loader";
import Pagination from "@/components/ui/Pagination/pagination";

// icons
import { LuCopy, LuCopyCheck } from "react-icons/lu";
import { FiSearch } from "react-icons/fi";
import { PiWarningDuotone } from "react-icons/pi";
import { IoCloseOutline } from "react-icons/io5";
import InputFileHidden from "../form/InputFileHidden/input.file.hidden";
import useUploadFile from "@/hooks/useUploadFile";
import { selectUploading } from "@/utils/store/features/data/upload.slice";
import { galleryServices } from "@/app/services/api/gallery.services";
import { useSearchParams } from "next/navigation";

/**
 * @type {React.FC <{
 *   initialData?: any[];
 *   onSelected?: Function;
 * }>}
 * @returns {JSX.Element}
 */
const GalleryComponent = (props) => {
  const dispatch = useDispatch();
  const searchref = useRef(null);

  const [galleryData, setGalleryData] = useState(props.initialData.data);
  const [selected, setSelected] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [uploaded, setUploaded] = useState([]);
  const [uploadingCount, setUploadingCount] = useState(0);

  const uploading = useSelector(selectUploading);
  const searchParams = useSearchParams();

  const {
    isLoading: loadSearch,
    isSearch,
    handleInput,
    handleSubmit,
    setInitialData,
    setSearchFn,
    setSetDataFn,
    cancel,
  } = useSearch();
  const { deleteconfirmMultipe } = useGalleryManagement();
  const {
    addFile,
    status,
    isRunning: uploadOnProgress,
  } = useUploadFile(
    { url: "/api/upload" },
    (data) => {
      const galleryData = data.files?.[0];

      setGalleryData((prev) => [galleryData, ...prev]);
      setUploaded((prev) => [...prev, galleryData]);
    },
    (error) => console.log({ error })
  );

  const progress = useMemo(() => {
    const totalProgress = uploading.reduce(
      (acc, item) => acc + item.progress,
      0
    );
    return totalProgress / uploading.length;
  }, [uploading]);

  const galleries = useMemo(() => {
    return galleryData.map((media) =>
      modifyByKeys(media, ["createdAt"], (date) => formatDate(date, true))
    );
  }, [galleryData]);

  const handleEdit = useCallback(
    (component, data) => {
      const id = "gallery_item_editing";
      dispatch(hideMiniWindowBy({ role: "id", value: id }));

      dispatch(
        addMiniWindow({
          status: true,
          component,
          data,
          id,
          onUpdate: (result) => {
            setGalleryData((prev) =>
              prev.map((data) => (data._id === result._id ? result : data))
            );
          },
          onDelete: (result) => {
            setGalleryData((prev) =>
              prev.filter((data) => data._id !== result._id)
            );
            setSelected((prev) =>
              prev.filter((data) => data._id !== result._id)
            );
          },
          height: "fit-content",
          ...(props.onSelected && {
            onSelect: (result) => {
              props.onSelected(result);
            },
          }),
        })
      );
    },
    [dispatch, props]
  );

  const getSortedData = useCallback(
    async ({ page, limit }, sort) => {
      try {
        const data = await galleryServices.getGallery({ page, limit }, sort);

        setGalleryData(data.data);
      } catch (error) {
        dispatch(
          setNotification({
            status: true,
            type: "error",
            message: "Getting sorted data failed! Contact Administrator.",
            timeout: 3000,
          })
        );
      }
    },
    [dispatch]
  );

  useEffect(() => {
    setIsLoading(loadSearch);
  }, [loadSearch]);

  return (
    <div className={styles.gallery_container}>
      <div className={styles.gallery_header}>
        <div className={styles.header_wrapper}>
          <h1>Gallery Management</h1>
          <div className={styles.gallery_action}>
            <div className={styles.gallery_status}>
              {Number.isInteger(parseInt(progress)) && (
                <p>
                  Uploading:{" "}
                  <span className={styles.action}>
                    {uploadingCount} file(s) - {progress.toFixed(2)}%
                  </span>{" "}
                </p>
              )}
              {selected.length > 0 && (
                <>
                  <p>
                    Selected:{" "}
                    <span>
                      {selected.length} item{selected.length > 1 && "s"}
                    </span>{" "}
                  </p>
                  <p>
                    <span
                      className={styles.action}
                      onClick={() => {
                        handleEdit(ItemGalleryMultipleEdit, selected);
                      }}>
                      Edit
                    </span>{" "}
                  </p>
                  <p>
                    <span
                      className={styles.action}
                      onClick={() =>
                        deleteconfirmMultipe(selected, (result) => {
                          const deletedIds = result.map((data) => data._id);
                          // if all data is deleted
                          if (galleryData.length === deletedIds.length)
                            window.location.reload();

                          setSelected([]);

                          return setGalleryData((prev) =>
                            prev.filter(
                              (data) => !deletedIds.includes(data._id)
                            )
                          );
                        })
                      }>
                      Delete
                    </span>{" "}
                  </p>
                </>
              )}
            </div>
            <InputFileHidden
              onChange={(files) => {
                setUploadingCount(files.length);
                addFile(files);
              }}>
              {(_, onClick) => {
                return (
                  <Button
                    className={styles.gallery_button}
                    disabled={Number.isInteger(progress) && progress < 100}
                    onClick={onClick}>
                    {uploadOnProgress && <Loader color="#fff" />}
                    Upload Images
                  </Button>
                );
              }}
            </InputFileHidden>
          </div>
        </div>
        <div className={styles.header_wrapper}>
          <form
            className={styles.search_container}
            onSubmit={handleSubmit}>
            <FiSearch />
            <input
              type="text"
              ref={searchref}
              placeholder="Search Gallery"
              onInput={handleInput}
              defaultValue={props.initialData?.keyword || ""}
              onFocus={() => {
                if (!isSearch) setInitialData(galleryData);

                setSearchFn(async (keyword) => {
                  const result = await galleryServices.getGallery(
                    { page: 1, limit: 100 },
                    { createdAt: -1 },
                    keyword
                  );

                  return result.data;
                });
                setSetDataFn((data) => {
                  setGalleryData(data);
                });
              }}
            />
            {isSearch && (
              <IoCloseOutline
                onClick={() => {
                  searchref.current.value = "";
                  cancel();
                }}
                style={{ cursor: "pointer" }}
              />
            )}
          </form>
        </div>
      </div>
      {isLoading ? (
        <div className={styles.loader_container}>
          <div className={styles.loader}>
            <Loader
              color="#000"
              width={30}
            />
            <p>Getting data</p>
          </div>
        </div>
      ) : (
        <Table
          data={galleries}
          role={{
            preview: "Image",
            title: "Title",
            alt: "Alt",
            filename: "Filename",
            url: "URL",
            createdAt: "Date",
          }}
          mediaPreview={{
            preview: "url",
          }}
          sortRole={{
            canSorting: true,
            excludeSorting: ["preview", "url"],
            sortingFn: getSortedData,
          }}
          onClick={(data) => {
            handleEdit(ItemGalleryEdit, data);
          }}
          onSelected={(data) => {
            setSelected(data);
          }}
        />
      )}
      {!isSearch && (
        <div className={styles.pagination}>
          <Pagination
            totalPage={props.initialData.totalPage}
            currentPage={(() => {
              const page = parseInt(
                Object.fromEntries(searchParams.entries())?.page
              );
              return page || props.initialData.page;
            })()}
            getData={galleryServices.getGallery}
            getIsLoading={(status) => setIsLoading(status)}
            setData={(data) => setGalleryData(data.data)}
            limit={50}
          />
        </div>
      )}
    </div>
  );
};

/**
 *
 * @type {React.FC<{
 *    data: any[];
 *    onUpdate: Function;
 *    onDelete: Function;
 *    onSelect: Function;
 * }>}
 * @returns
 */
export const ItemGalleryMultipleEdit = (props) => {
  const dispatch = useDispatch();
  const [data, setData] = useState(props.data);
  const [currentIndex, setCurrentIndex] = useState(0);
  const currentData = useMemo(() => data[currentIndex], [currentIndex, data]);

  const handleDelete = useCallback(() => {
    const current = data.filter((_, index) => index !== currentIndex);
    if (current.length === 0) {
      dispatch(hideMiniWindowBy({ role: "id", value: "gallery_item_editing" }));
      return;
    }

    setData(current);
  }, [data, currentIndex, dispatch]);

  return (
    <div className={styles.item_multiple_edit}>
      <div className={styles.item_navigation}>
        {data.map((item, index) => {
          return (
            <div
              className={`${styles.navigation_item} ${
                index === currentIndex && styles.active
              }`}
              key={index}
              onClick={() => setCurrentIndex(index)}>
              <SafeImage
                src={item.url}
                alt={item.title}
                className={styles.navigation_image}
                width={100}
                height={100}
                styles={{ width: "auto", height: "auto" }}
                fallbackSrc="https://placehold.co/300x200/f0f0f0/999/png?text=NotFound"
              />
              <p>{item.title}</p>
            </div>
          );
        })}
      </div>
      <ItemGalleryEdit
        data={currentData}
        onUpdate={(data) => {
          setData((prev) =>
            prev.map((item, index) => (index === currentIndex ? data : item))
          );
          props.onUpdate(data);
        }}
        onDelete={(data) => {
          handleDelete();
          setTimeout(() => props.onDelete(data));
        }}
        onSelect={props.onSelect}
      />
    </div>
  );
};

/**
 *
 * @type {React.FC<{
 *    data: any;
 *    onUpdate: Function;
 *    onDelete: Function;
 *    onSelect: Function;
 * }>}
 * @returns
 */
export const ItemGalleryEdit = (props) => {
  const dispatch = useDispatch();
  const [data, setData] = useState(props.data);
  const [isUpdated, setIsUpdated] = useState(false);
  const [mediaAvailable, setMediaAvailable] = useState(true);
  const [copied, setCopied] = useState({
    filename: false,
    url: false,
  });
  const { update, deleteconfirm } = useGalleryManagement();

  const isChanged = useMemo(() => {
    return JSON.stringify(data) !== JSON.stringify(props.data);
  }, [data, props.data]);
  const isValidData = useMemo(() => {
    const role = ["title", "alt", "url"];
    return {
      status: role.every((v) => data[v]) || role.every((v) => props.data[v]),
      key: role.find((v) => !data[v]) || role.find((v) => !props.data[v]),
    };
  }, [props.data, data]);

  const handleUpdate = useCallback(async () => {
    const currentData = modifyByKeys(
      data,
      ["url"],
      (url) => `/media${url.split("media")[1]}`
    );
    await update(currentData, (result) => {
      if (props.onUpdate && typeof props.onUpdate === "function")
        props.onUpdate(result);
      setData(result);
      setIsUpdated(true);
    });
  }, [data, props, update]);

  useEffect(() => {
    if (Object.values(copied).some((v) => v))
      setTimeout(() => {
        setCopied({ filename: false, url: false });
      }, 3000);
  }, [copied]);

  useEffect(() => {
    setData(props.data);
  }, [props.data]);

  return (
    <div className={styles.item_container}>
      <div className={styles.item_header}>
        <h1>{props.data?.title}</h1>
      </div>
      <div className={styles.item_action}>
        <div className={styles.item_info}>
          <p>
            ID: <span>{props.data?._id}</span>
          </p>
          <p>
            Uploaded At: <span>{props.data?.createdAt}</span>
          </p>
          {isChanged && mediaAvailable && (
            <p>
              status: <span>Changed</span>
            </p>
          )}
          {!mediaAvailable && props.onSelect && (
            <p>
              Error: <span>You Can`t Select This Media</span>
            </p>
          )}
        </div>
        <div className={styles.item_action_button}>
          {props.onSelect &&
            typeof props.onSelect === "function" &&
            mediaAvailable && (
              <Button
                className={styles.button}
                onClick={() => {
                  if (props.onSelect && typeof props.onSelect === "function")
                    props.onSelect(data);
                }}
                disabled={!isValidData.status || (!isUpdated && isChanged)}>
                Select
              </Button>
            )}
          {isChanged && (
            <Button
              className={styles.button}
              onClick={async () => {
                await handleUpdate();
              }}>
              Update
            </Button>
          )}
          <Button
            className={styles.button}
            onClick={async () =>
              deleteconfirm(props.data, (result) => {
                props.onDelete(result);
                dispatch(hideMiniWindowOnIndex(props.index));
              })
            }>
            Delete
          </Button>
        </div>
      </div>
      <div className={styles.item_body}>
        <div className={styles.body_image}>
          <SafeImage
            src={props.data?.url}
            alt={props.data?.alt || props.data?.title}
            className={styles.image}
            width={500}
            height={500}
            onClick={() => {
              window.open(props.data?.url, "_blank", "noopener,noreferrer");
            }}
            getIsError={(error) => {
              console.log({ error });
              setMediaAvailable(!error);
            }}
            fallbackSrc="https://placehold.co/300x200/f0f0f0/999/png?text=NotFound"
          />
        </div>
        <div className={styles.body_content}>
          <div className={styles.item_box}>
            <p className={`${styles.label} ${styles.item_box_label}`}>
              URL{" "}
              <span>
                {copied.url ? (
                  <LuCopyCheck />
                ) : (
                  <LuCopy
                    onClick={() => {
                      window.navigator.clipboard.writeText(props.data?.url);
                      dispatch(
                        setNotification({
                          status: true,
                          message: "URL Copied to clipboard",
                          timeout: 3000,
                        })
                      );
                      setCopied((prev) => ({ ...prev, url: true }));
                    }}
                  />
                )}
              </span>
            </p>
            <p
              className={styles.item_box_value}
              onClick={() => {
                window.open(props.data?.url, "_blank", "noopener,noreferrer");
              }}>
              {props.data?.url}
            </p>
          </div>
          <div className={styles.item_box}>
            <p className={`${styles.label} ${styles.item_box_label}`}>
              Filename{" "}
              <span>
                {copied.filename ? (
                  <LuCopyCheck />
                ) : (
                  <LuCopy
                    onClick={() => {
                      window.navigator.clipboard.writeText(
                        props.data?.filename
                      );
                      dispatch(
                        setNotification({
                          status: true,
                          message: "Filename Copied to clipboard",
                          timeout: 3000,
                        })
                      );
                      setCopied((prev) => ({ ...prev, filename: true }));
                    }}
                  />
                )}
              </span>
            </p>
            <p className={styles.item_box_value}>{props.data?.filename}</p>
          </div>
          <Input
            label="Title"
            value={data?.title || ""}
            className={styles.input}
            labelclassname={styles.label}
            onChange={(e) =>
              setData((prev) => ({ ...prev, title: e.target.value }))
            }
          />
          <Input
            label="Alt"
            value={data.alt || ""}
            className={styles.input}
            onChange={(e) =>
              setData((prev) => ({ ...prev, alt: e.target.value }))
            }
            labelclassname={styles.label}
          />
        </div>
      </div>
      {!isValidData.status && (
        <div className={styles.warning}>
          <p className={styles.warning_text}>
            <span>
              <PiWarningDuotone /> Warning:
            </span>
            {isValidData.key} is missing from your data. Please update your
            data!
          </p>
        </div>
      )}
    </div>
  );
};

const Gallery = memo(GalleryComponent);
export default Gallery;
