import type { ComponentProcessProps } from "components/system/Apps/RenderComponent";
import StyledButton from "components/system/Dialogs/Transfer/StyledButton";
import StyledTransfer from "components/system/Dialogs/Transfer/StyledTransfer";
import type {
  FileReaders,
  ObjectReaders,
} from "components/system/Dialogs/Transfer/useTransferDialog";
import { useProcesses } from "contexts/process";
import { basename, dirname } from "path";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { ONE_TIME_PASSIVE_EVENT } from "utils/constants";
import { haltEvent } from "utils/functions";

const MAX_TITLE_LENGTH = 37;

const isFileReaders = (
  readers?: FileReaders | ObjectReaders
): readers is FileReaders => Array.isArray(readers?.[0]);

const Transfer: FC<ComponentProcessProps> = ({ id }) => {
  const {
    argument,
    closeWithTransition,
    processes: { [id]: process } = {},
    title,
  } = useProcesses();
  const { closing, fileReaders, url } = process || {};
  const [currentTransfer, setCurrentTransfer] = useState<[string, File]>();
  const [cd = "", { name = "" } = {}] = currentTransfer || [];
  const [progress, setProgress] = useState<number>(0);
  const actionName = useMemo(
    () => (url && !fileReaders ? "Extraindo" : "Copiando"),
    [fileReaders, url]
  );
  const processing = useRef(false);
  const completeTransfer = useCallback(() => {
    processing.current = false;
    closeWithTransition(id);
  }, [closeWithTransition, id]);
  const processObjectReader = useCallback(
    ([reader, ...remainingReaders]: ObjectReaders) => {
      const isComplete = remainingReaders.length === 0;

      reader.read().then(() => {
        setProgress((currentProgress) => currentProgress + 1);

        if (isComplete) {
          reader.done?.();
          completeTransfer();
        } else {
          const [{ directory, name: nextName }] = remainingReaders;

          setCurrentTransfer([directory, { name: nextName } as File]);
        }
      });

      if (!isComplete) processObjectReader(remainingReaders);
    },
    [completeTransfer]
  );
  const processFileReader = useCallback(
    ([[file, directory, reader], ...remainingReaders]: FileReaders) => {
      let fileProgress = 0;

      setCurrentTransfer([directory, file]);

      reader.addEventListener(
        "progress",
        ({ loaded = 0 }) => {
          const progressLoaded = loaded - fileProgress;

          setProgress((currentProgress) => currentProgress + progressLoaded);
          fileProgress = loaded;
        },
        { passive: true }
      );
      reader.addEventListener(
        "loadend",
        () => {
          if (remainingReaders.length > 0) {
            processFileReader(remainingReaders);
          } else {
            completeTransfer();
          }
        },
        ONE_TIME_PASSIVE_EVENT
      );
      reader.readAsArrayBuffer(file);
    },
    [completeTransfer]
  );
  const totalTransferSize = useMemo(
    () =>
      isFileReaders(fileReaders)
        ? fileReaders.reduce((acc, [{ size = 0 }]) => acc + size, 0)
        : fileReaders?.length || Number.POSITIVE_INFINITY,
    [fileReaders]
  );

  useEffect(() => {
    if (!processing.current) {
      if (fileReaders) {
        if (fileReaders?.length > 0) {
          processing.current = true;

          if (isFileReaders(fileReaders)) {
            processFileReader(fileReaders);
          } else {
            const [{ directory, name: firstName }] = fileReaders;

            setCurrentTransfer([directory, { name: firstName } as File]);
            processObjectReader(fileReaders);
          }
        } else {
          closeWithTransition(id);
        }
      } else if (url) {
        setCurrentTransfer([dirname(url), { name: basename(url) } as File]);
      }
    }
  }, [
    closeWithTransition,
    fileReaders,
    id,
    processFileReader,
    processObjectReader,
    url,
  ]);

  useEffect(() => {
    if (processing.current) {
      const progressPercent = Math.floor((progress / totalTransferSize) * 100);

      argument(id, "progress", progressPercent);
      title(id, `${progressPercent}% completo`);
    }
  }, [argument, id, progress, title, totalTransferSize]);

  useEffect(() => title(id, `${actionName}...`), [actionName, id, title]);

  useEffect(
    () => () => {
      if (closing && processing.current) {
        if (isFileReaders(fileReaders)) {
          // eslint-disable-next-line unicorn/no-unreadable-array-destructuring
          fileReaders.forEach(([, , reader]) => reader.abort());
        } else {
          fileReaders?.forEach((reader) => reader.abort());
          fileReaders?.[0]?.done?.();
        }
      }
    },
    [closing, fileReaders]
  );

  return (
    <StyledTransfer onContextMenu={haltEvent}>
      <h1>
        {name
          ? `${actionName} '${
              name.length >= MAX_TITLE_LENGTH
                ? `${name.slice(0, MAX_TITLE_LENGTH)}...`
                : name
            }'`
          : ""}
      </h1>
      <div>
        <h2>{cd ? `Para '${cd}'` : ""}</h2>
        <progress
          max={totalTransferSize}
          value={
            totalTransferSize === Number.POSITIVE_INFINITY
              ? undefined
              : progress
          }
        />
      </div>
      <nav>
        <StyledButton onClick={() => closeWithTransition(id)}>
          Cancel
        </StyledButton>
      </nav>
    </StyledTransfer>
  );
};

export default Transfer;
