import { use, useEffect, useRef, useState, FC } from "react";

import { reaction, toJS } from "mobx";
import { observer } from "mobx-react-lite";

import { addSeconds } from "date-fns/addSeconds";
import { differenceInSeconds } from "date-fns/differenceInSeconds";

import { CSSTransition } from "react-transition-group";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faPause } from "@fortawesome/free-solid-svg-icons/faPause";
import { faPlay } from "@fortawesome/free-solid-svg-icons/faPlay";

import { AlertStore, AlertStoreStatuses } from "Stores/AlertStore";
import type { Settings } from "Stores/Settings";
import { ThemeContext } from "Components/Theme";
import { TooltipWrapper } from "Components/TooltipWrapper";

const PauseButton: FC<{ alertStore: AlertStore }> = ({ alertStore }) => {
  const context = use(ThemeContext);
  const nodeRef = useRef<HTMLSpanElement>(null);
  return (
    <TooltipWrapper title="Click to resume updates">
      <CSSTransition
        in={true}
        appear={true}
        classNames="components-animation-fade"
        timeout={context.animations.duration}
        nodeRef={nodeRef}
      >
        <span ref={nodeRef} className="d-inline-block">
          <FontAwesomeIcon
            className="cursor-pointer text-muted components-fetcher-icon mx-2 fa-fw"
            icon={faPause}
            onClick={alertStore.status.resume}
          />
        </span>
      </CSSTransition>
    </TooltipWrapper>
  );
};

const PlayButton: FC<{ alertStore: AlertStore }> = ({ alertStore }) => {
  const context = use(ThemeContext);
  const nodeRef = useRef<HTMLSpanElement>(null);
  return (
    <TooltipWrapper title="Click to pause updates">
      <CSSTransition
        in={true}
        appear={true}
        classNames="components-animation-fade"
        timeout={context.animations.duration}
        nodeRef={nodeRef}
      >
        <span ref={nodeRef} className="d-inline-block">
          <FontAwesomeIcon
            className="cursor-pointer text-muted components-fetcher-icon mx-2 fa-fw"
            icon={faPlay}
            onClick={alertStore.status.pause}
          />
        </span>
      </CSSTransition>
    </TooltipWrapper>
  );
};

const Dots: FC<{ alertStore: AlertStore; dots: number }> = observer(
  ({ alertStore, dots }) => {
    return (
      <div
        className={`cursor-pointer components-fetcher ${
          alertStore.info.isRetrying ? "retrying" : ""
        } ${
          alertStore.status.value.toString() ===
          AlertStoreStatuses.Processing.toString()
            ? "processing"
            : ""
        } ${
          dots === 0 ||
          alertStore.status.value.toString() ===
            AlertStoreStatuses.Fetching.toString()
            ? "fetching"
            : ""
        }`}
      >
        {Array.from(Array(9).keys()).map((i) => (
          <div
            key={i}
            className={`dot ${i === 4 ? "dot-middle" : ""} ${
              i < dots ? "visible" : "hidden"
            }`}
          ></div>
        ))}
      </div>
    );
  },
);

const Fetcher: FC<{
  alertStore: AlertStore;
  settingsStore: Settings;
}> = observer(({ alertStore, settingsStore }) => {
  const timerRef = useRef<number | undefined>(undefined);
  const [percentLeft, setPercentLeft] = useState<number>(100);
  const [isHover, setIsHover] = useState(false);

  const getSortSettings = () => {
    const sortSettings = {
      useDefaults: false,
      sortOrder: "",
      sortLabel: "",
      sortReverse: "",
    };

    sortSettings.useDefaults =
      settingsStore.gridConfig.config.sortOrder ===
      settingsStore.gridConfig.options.default.value;

    if (sortSettings.useDefaults === true) {
      return sortSettings;
    }

    sortSettings.sortOrder = settingsStore.gridConfig.config.sortOrder;

    // don't sort if sorting is disabled
    if (
      sortSettings.sortOrder === settingsStore.gridConfig.options.disabled.value
    )
      return sortSettings;

    sortSettings.sortReverse =
      settingsStore.gridConfig.config.reverseSort !== null
        ? settingsStore.gridConfig.config.reverseSort === true
          ? "1"
          : "0"
        : "";

    if (settingsStore.gridConfig.config.sortLabel !== null) {
      sortSettings.sortLabel = settingsStore.gridConfig.config.sortLabel;
    }

    return sortSettings;
  };

  const fetchIfIdle = () => {
    const now = new Date();

    const nextTick = addSeconds(
      alertStore.status.lastUpdateAt,
      settingsStore.fetchConfig.config.interval,
    );

    const secondsLeft = differenceInSeconds(nextTick, now);

    setPercentLeft(
      Math.max(
        0,
        (secondsLeft / settingsStore.fetchConfig.config.interval) * 100,
      ),
    );

    const pastDeadline = now >= nextTick;

    const status = alertStore.status.value.toString();
    const updateInProgress =
      status === AlertStoreStatuses.Fetching.toString() ||
      status === AlertStoreStatuses.Processing.toString();

    if (pastDeadline && !updateInProgress && !alertStore.status.paused) {
      callFetch();
    }
  };

  const callFetch = () => {
    if (alertStore.status.paused) return;

    const sortSettings = getSortSettings();
    alertStore.fetchWithThrottle(
      settingsStore.multiGridConfig.config.gridLabel,
      settingsStore.multiGridConfig.config.gridSortReverse,
      sortSettings.sortOrder,
      sortSettings.sortLabel,
      sortSettings.sortReverse === "1",
      Object.values(alertStore.ui.gridGroupLimits).length > 0
        ? toJS(Object.values(alertStore.ui.gridGroupLimits)[0])
        : {},
      settingsStore.alertGroupConfig.config.defaultRenderCount,
      alertStore.ui.groupAlertLimits,
    );
  };

  useEffect(() => {
    return () => window.clearInterval(timerRef.current);
  }, []);

  useEffect(
    () =>
      reaction(
        () =>
          JSON.stringify({
            filters: alertStore.filters.values
              .map((f: { raw: string }) => f.raw)
              .join(" "),
            grid: {
              sortOrder: toJS(settingsStore.gridConfig.config.sortOrder),
              sortLabel: toJS(settingsStore.gridConfig.config.sortLabel),
              gridGroupLimits: toJS(alertStore.ui.gridGroupLimits),
              defaultGroupLimit: toJS(
                settingsStore.alertGroupConfig.config.defaultRenderCount,
              ),
              groupAlertLimits: toJS(alertStore.ui.groupAlertLimits),
            },
            multigrid: {
              gridLabel: toJS(settingsStore.multiGridConfig.config.gridLabel),
              gridSortReverse: toJS(
                settingsStore.multiGridConfig.config.gridSortReverse,
              ),
              reverseSort: toJS(settingsStore.gridConfig.config.reverseSort),
            },
          }),
        () => {
          callFetch();
        },
        { fireImmediately: true },
      ),
    [],
  );

  useEffect(
    () =>
      reaction(
        () => alertStore.status.paused,
        (paused) => {
          if (paused) {
            window.clearInterval(timerRef.current);
            timerRef.current = undefined;
          } else {
            timerRef.current = window.setInterval(
              () => window.requestAnimationFrame(fetchIfIdle),
              1000,
            );
          }
        },
        { fireImmediately: true },
      ),
    [],
  );

  const dots = Math.max(0, Math.min(9, percentLeft / 10));

  return (
    <div
      className="navbar-brand py-0 me-2 d-none d-sm-block"
      onMouseEnter={() => setIsHover(true)}
      onMouseLeave={() => setIsHover(false)}
    >
      {alertStore.info.upgradeNeeded ? null : alertStore.status.paused ? (
        <PauseButton alertStore={alertStore} />
      ) : isHover ? (
        <PlayButton alertStore={alertStore} />
      ) : (
        <Dots alertStore={alertStore} dots={dots} />
      )}
    </div>
  );
});

export { Fetcher, Dots };
