import { Fragment, memo, useEffect, useRef, useState } from "react";
import {
  BiDotsHorizontalRounded,
  BiExit,
  BiSolidUser,
  BiPencil,
  BiLink,
  BiWrench,
} from "react-icons/bi";
import Logo from "../assets/logo.svg";
import LogoAlternative from "../assets/fully_alternative_logo.svg";
import {
  // AnimatedCanvas,
  LoadingSpinner,
  DatePicker,
  TextPreloader,
  // CoolMouseBg,
} from "../trash/trash-components";
import { AdminReq, classUserFRequest, RaspGet } from "../trash/req_mgr";
import { AnimatePresence, LayoutGroup, motion } from "motion/react";

import { apiFormatDate, isTimeBeforeNow } from "../trash/dengi";
import { useAtom } from "jotai";
import { API_BASE_URL } from "../App";
import { copyDXLink, ifstyle } from "../trash/helpers";
import { useWebHaptics } from "web-haptics/react";
import { DengiCaching } from "../trash/dengiCashingSystem";

/**
 * @param {{classIDAtom,adminAtom,setsetRoute,routeAtom,pickmeModeAtom,dengi_cash: DengiCaching}} param0
 */
export function DX4App({
  classIDAtom,
  adminAtom,
  setsetRoute,
  routeAtom,
  pickmeModeAtom,
  dengi_cash,
}) {
  const [classId, setUserId] = useAtom(classIDAtom);
  const [adminId, _setAdminId] = useAtom(adminAtom);
  const [openUserSettings, setOpenUserSettings] = useState(false);
  const setRoute = useAtom(routeAtom)[1];
  const [pickmeMode, setPickmeMode] = useAtom(pickmeModeAtom);
  const [rasp, setRasp] = useState([false, {}, 0]);
  const [selectedDate, setSelectedDate] = useState(new Date());
  const [managing, setManaging] = useState(false);

  useEffect(() => {
    let cancelled = false;
    setRasp([false, {}]);
    const cacheName = `rasp-${classId}-${selectedDate.toDateString()}`;
    if (!dengi_cash.wasCached(cacheName)) {
      RaspGet(API_BASE_URL, classId, selectedDate).then(
        (d) => {
          if (d[0] == 1) {
            try {
              if (!cancelled)
                setRasp([
                  true,
                  d[1]["data"] && d[1]["data"][0] ? d[1]["data"] : -322,
                  d[1]["type"] ? d[1]["type"] : -1,
                ]);
              //console.log(d, d[1] ? d[1] : false);
              dengi_cash.cache(cacheName, [
                d[1]["data"] && d[1]["data"][0] ? d[1]["data"] : -322,
                d[1]["type"] ? d[1]["type"] : -1,
              ]);
            } catch (exception) {
              //console.error(exception);
              if (!cancelled) setRasp([true, exception.toString()]);
            }
          } else {
            if (!cancelled) setRasp([true, "ошибка: " + d[1].toString()]);
            //console.log(d[1]);
          }
        },
        (err) => {
          if (cancelled) return;
          setRasp([true, "ошибка: " + err]);
        },
      );
    } else {
      if (cancelled) return;
      setRasp([true, ...dengi_cash.get(cacheName)]);
    }

    return () => {
      cancelled = true;
    };
    //console.log(dengi_cash);
  }, [selectedDate]);

  const [userFrNames, setUserFrNames] = useState({
    admin: false,
    class: false,
  });

  const lessonsEndedTriggered = useRef(false);

  //TODO: Auto change day v2
  useEffect(() => {
    if (
      lessonsEndedTriggered.current ||
      typeof rasp[1] != "object" ||
      rasp[1].lenght <= 0 ||
      apiFormatDate(selectedDate) !== apiFormatDate(new Date())
    )
      return;
    try {
      if (!rasp[1].at) return;
      const tEnd = rasp[1].at(-1)["t-end"];
      if (!/\d{1,2}:\d{2}$/.test(tEnd)) return; // not time type
      if (isTimeBeforeNow(tEnd)) {
        //console.log(true);
        const d = new Date(selectedDate);
        d.setDate(d.getDate() + 1);
        setSelectedDate(d);

        lessonsEndedTriggered.current = true;

        console.log("LOG: Auto change day triggired");
      }
    } catch (err) {
      console.error(err);
      return;
    }
  }, [rasp]);
  useEffect(() => {
    (async () => {
      let cancelled = false;
      const promises = [];
      if (classId !== "")
        promises.push(
          dengi_cash.withCacheAsync(`userfr-${classId}`, async () =>
            classUserFRequest(API_BASE_URL, classId),
          ),
        );
      else promises.push(Promise.resolve([0]));
      if (adminId !== "")
        promises.push(
          dengi_cash.withCacheAsync(`admindata-${adminId}`, async () =>
            AdminReq(API_BASE_URL, adminId),
          ),
        );
      else promises.push(Promise.resolve([0]));
      Promise.all(promises).then(([classRes, adminRes]) => {
        if (cancelled) return;
        const classF = classRes[0] === 1 ? classRes[1].userclassname : false;
        const adminF = adminRes[0] === 1 ? adminRes[1].adminName : false;
        setUserFrNames({ admin: adminF, class: classF });
        if (adminRes[0] === 1)
          setManaging([-1, 2].includes(adminRes[1].adminPrems));
      });
      return () => {
        cancelled = true;
      };
    })();
  }, [classId, adminId]);

  /**
   * @param {KeyboardEvent} event
   */
  const handleKeyPress = (event) => {
    switch (event.key) {
      case "a":
      case "ArrowLeft":
        setSelectedDate((prevDate) => {
          const newDate = new Date(prevDate);
          newDate.setDate(newDate.getDate() - 1);
          return newDate;
        });
        break;
      case "d":
      case "ArrowRight":
        setSelectedDate((prevDate) => {
          const newDate = new Date(prevDate);
          newDate.setDate(newDate.getDate() + 1);
          return newDate;
        });
        break;
      default:
        break;
    }
  };

  useEffect(() => {
    window.addEventListener("keydown", handleKeyPress);

    if(window['DX_OPEN_DATE']){
      setSelectedDate(window['DX_OPEN_DATE'])
      window['DX_OPEN_DATE'] = undefined
    }

    return () => {
      window.removeEventListener("keydown", handleKeyPress);
    };
  }, []);

  if (classId === "") {
    setsetRoute(setRoute, "/");
  }
  const { trigger } = useWebHaptics();

  const raspDragStartX = useRef(0);
  return (
    <>
      <motion.div className="w-full h-screen flex flex-col items-center justify-center gap-5 overflow-x-hidden z-2">
        <motion.div
          className="w-screen h-screen flex flex-col items-center sm:max-w-[500px] sm:max-h-[90vh] gap-3"
          initial={{ scale: 0.99, opacity: 0, y: 5 }}
          animate={{ scale: 1, opacity: 1, y: 0 }}
        >
          <motion.div
            className={
              "flex items-center w-full justify-between px-5" +
              ifstyle(openUserSettings, " pt-5")
            }
          >
            <motion.img
              src={pickmeMode ? LogoAlternative : Logo}
              alt="logo"
              width={100}
              draggable={false}
            />
            <motion.div className="flex gap-5 items-center">
              <AnimatePresence mode="popLayout">
                {!openUserSettings && (
                  <motion.span
                    className="font-light"
                    variants={{ hidden: { opacity: 0 }, show: { opacity: 1 } }}
                    initial="hidden"
                    animate="show"
                    exit="hidden"
                  >
                    {userFrNames["class"] ? userFrNames["class"] : classId}
                  </motion.span>
                )}
              </AnimatePresence>
              <motion.span
                className={
                  "rounded-full w-fit h-fit" +
                  ifstyle(openUserSettings, "border")
                }
              >
                <BiDotsHorizontalRounded
                  size={35}
                  onClick={() => {
                    setOpenUserSettings(!openUserSettings);
                    trigger([{ duration: 10 }, { delay: 100, duration: 10 }], {
                      intensity: 1,
                    });
                  }}
                  onContextMenu={(e) => {
                    e.preventDefault();
                    setPickmeMode(!pickmeMode);
                  }}
                  className={
                    "rounded-full " +
                    (pickmeMode
                      ? "lg:hover:bg-pink-400"
                      : "lg:hover:bg-white lg:hover:fill-black") +
                    " lg:hover:scale-105 transition cursor-pointer"
                  }
                />
              </motion.span>
              <AnimatePresence mode="popLayout">
                <UserSettings
                  {...{
                    openUserSettings,
                    userFrNames,
                    setUserId,
                    setsetRoute,
                    setRoute,
                    adminId,
                    classId,
                    pickmeMode,
                  }}
                />
              </AnimatePresence>
            </motion.div>
          </motion.div>

          {adminId != "" && managing && (
            <motion.div
              className="flex w-full justify-end not-md:px-4"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
            >
              <a
                // onClick={() => {
                //   setsetRoute(setRoute, `/editor/${apiFormatDate(selectedDate)}`);
                // }}
                href={`#/classprofile`}
                className={`flex items-center justify-center border p-1 gap-2 px-2 cursor-pointer rounded-xl transition-colors ${
                  pickmeMode
                    ? "border-pink-400/30 text-pink-300/80 lg:hover:text-white lg:hover:border-pink-500"
                    : "border-zinc-700 text-zinc-400 lg:hover:text-white lg:hover:border-white"
                } `}
              >
                <BiWrench /> управление
              </a>
            </motion.div>
          )}
          <motion.div
            className="flex flex-col gap-5 w-full not-md:px-4"
          >
            <motion.div className="flex gap-2 items-center justify-center">
              <DatePicker
                date={selectedDate}
                setDate={setSelectedDate}
                indicator={
                  typeof rasp[2] == "number" && [1, 2].includes(rasp[2])
                }
                pink={pickmeMode}
              />
              {adminId != "" && (
                <a
                  // onClick={() => {
                  //   setsetRoute(setRoute, `/editor/${apiFormatDate(selectedDate)}`);
                  // }}
                  href={
                    userFrNames.admin
                      ? `#/editor/${apiFormatDate(selectedDate)}`
                      : undefined
                  }
                  className={`flex items-center justify-center  p-1.5 cursor-pointer border transition *:transition-colors rounded-full ${
                    pickmeMode
                      ? "border-pink-400/30 lg:hover:text-white hover:lg:bg-pink-500"
                      : "border-zinc-700 text-white lg:hover:text-black lg:hover:bg-white"
                  } `}
                >
                  {userFrNames.admin ? (
                    <BiPencil size={28} />
                  ) : (
                    <LoadingSpinner size={28} />
                  )}
                </a>
              )}
            </motion.div>
            <motion.div
              className="relative min-h-[50vh]"
              drag="x"
              dragConstraints={{ top: 0, right: 0, bottom: 0, left: 0 }}
              dragElastic={0.2}
              onDragStart={(ev) => {
                raspDragStartX.current = ev.clientX;
              }}
              onDragEnd={(ev) => {
                if (raspDragStartX.current > ev.clientX + 150) {
                  const d = new Date(selectedDate);
                  d.setDate(d.getDate() + 1);
                  setSelectedDate(d);
                  trigger([{ duration: 10 }], { intensity: 1 });
                } else if (raspDragStartX.current < ev.clientX - 150) {
                  const d = new Date(selectedDate);
                  d.setDate(d.getDate() - 1);
                  setSelectedDate(d);
                  trigger([{ duration: 10 }], { intensity: 1 });
                }
              }}
            >
              <LayoutGroup>
                <RaspView {...{ rasp, pickmeMode, selectedDate }} />
              </LayoutGroup>
              {!rasp[0] && (
                <motion.div
                  className="w-full flex justify-center"
                  initial="hid"
                  animate="show"
                  variants={{
                    show: { opacity: 1, scale: 1 },
                    hid: { opacity: 0, scale: 0 },
                  }}
                >
                  <LoadingSpinner size={25} />
                </motion.div>
              )}
            </motion.div>
          </motion.div>
        </motion.div>
      </motion.div>
      <CoolMainBg {...{ pickmeMode }} />
    </>
  );
}
const CoolMainBg = memo(({ pickmeMode }) => {
  return (
    <div
      className={`fixed top-0 left-0 w-full h-full -z-5 overflow-hidden bg-gradient-to-br to-black to-50% bg-fixed
        ${pickmeMode ? "from-pink-400/10" : "from-white/2"}`}
    />
  );
});

export const RaspView = memo(({ rasp, pickmeMode, selectedDate }) => {
  return (
    <AnimatePresence mode="popLayout">
      {rasp[0] && typeof rasp[1] == "object" && (
        <motion.table
          //layoutId="schedule_table"
          //layout="position"
          key={selectedDate}
          className={
            "w-full border-collapse border-spacing-x-0 border-spacing-y-[10px] bg-gradient-to-br mb-10 " +
            (pickmeMode ? "from-pink-400/30" : "from-[#101011ee]") +
            " to-[#030303fa] rounded-2xl"
          }
          onContextMenu={(ev) => {
            ev.preventDefault();
            itemRedo.action();
          }}
          variants={{
            hid: { opacity: 0 },
            show: { opacity: 1 },
          }}
          initial="hid"
          animate="show"
          exit="hid"
        >
          <thead className="">
            <tr className=" *:text-center text-zinc-400 *:py-2 border-b *:my-5 *:min-w-15 *:lg:hover:text-zinc-200 *:transition-colors *:font-light">
              <th
                className="max-w-10"
                //layout="position"
                //layoutId="table-num-h"
              >
                n урока
              </th>
              <th
              //layout="position"
              //layoutId="table-subj-h"
              >
                предмет
              </th>
              <th
              //layout="position"
              //layoutId="table-cab-h"
              >
                каб
              </th>
              <th
              //layout="position"
              //layoutId="table-t-h"
              >
                <div className="flex items-center justify-center gap-2">
                  {rasp[2] == 2 && (
                    <span className="w-2 h-2 animate-pulse rounded-full bg-zinc-300 block" />
                  )}
                  время
                </div>
              </th>
            </tr>
          </thead>
          <tbody className="*:*:text-center *:*:py-2 *:not-last:border-b *:not-last:border-zinc-800 text-white ">
            {rasp[1].map((d, i) => (
              <TableItem {...{ d }} key={`table-header-${i}`} />
            ))}
          </tbody>
        </motion.table>
      )}
      {typeof rasp[1] == "number" && rasp[1] === -322 && (
        <motion.div
          className="w-full flex justify-center"
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
        >
          <span className="text-2xl font-bold">нет уроков</span>
        </motion.div>
      )}
      {typeof rasp[1] == "string" && rasp[1].startsWith("ошибка") && (
        <motion.div
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          className="w-full flex justify-center"
        >
          <span className="text-xl text-center text-transparent bg-clip-text bg-gradient-to-r from-red-300 to-red-500">
            {rasp[1]}
          </span>
        </motion.div>
      )}
    </AnimatePresence>
  );
});

const UserSettings = memo(
  ({
    openUserSettings,
    userFrNames,
    setUserId,
    setsetRoute,
    setRoute,
    adminId,
    classId,
    pickmeMode,
  }) => {
    const [classLinkCopied, setClassLinkCopied] = useState(false);

    return (
      <>
        {openUserSettings && (
          <motion.div
            className="flex flex-col bg-grain gap-2 bg-zinc-700/20 p-2 rounded-2xl"
            variants={{
              hidden: { opacity: 0, y: 10 },
              show: { opacity: 1, y: 0 },
            }}
            initial="hidden"
            animate="show"
            exit="hidden"
          >
            {[
              // icon
              // text
              // onclick
              // condition
              [
                <BiExit />,
                "выход",
                () => {
                  setUserId("");
                  setsetRoute(setRoute, "/");
                },
                true,
              ],
              // [
              //   <BiStar />,
              //   "пикми режим",
              //   () => {
              //     setPickmeMode(!pickmeMode);
              //   },
              //   true,
              // ],
              [
                <BiSolidUser />,
                <>
                  {`админ ${userFrNames["admin"] ? userFrNames["admin"] : ""}`}
                  {!userFrNames["admin"] && <TextPreloader width="30px" />}
                </>,
                () => {
                  setsetRoute(setRoute, "/adminprofile");
                },
                adminId != "",
              ],
              [
                <BiLink />,
                classLinkCopied ? "скопировано" : "коп. ссылку",
                () => {
                  copyDXLink(classId).then((a) => {
                    if (a) {
                      setClassLinkCopied(true);
                      setTimeout(() => {
                        setClassLinkCopied(false);
                      }, 2000);
                    }
                  });
                },
                classId != "",
              ],
            ].map(
              (item, i) =>
                item[3] && (
                  <div
                    key={i}
                    className={
                      "flex gap-2 items-center justify-center transition-colors " +
                      (pickmeMode
                        ? "lg:hover:bg-pink-400"
                        : "lg:hover:bg-white lg:hover:text-black") +
                      "  p-1 rounded-xl cursor-pointer"
                    }
                    onClick={item[2]}
                  >
                    {item[0]} {item[1]}
                  </div>
                ),
            )}
          </motion.div>
        )}
      </>
    );
  },
);

class GlobalListener {
  constructor() {
    this.listeners = [];
  }
  listen(fn, once = false) {
    const wrapper = (...args) => {
      try {
        fn(...args);
      } finally {
        if (once) this.listeners = this.listeners.filter((l) => l !== wrapper);
      }
    };
    this.listeners.push(wrapper);
    return () => {
      this.listeners = this.listeners.filter((l) => l !== wrapper);
    };
  }
  action(...args) {
    this.listeners.slice().forEach((fn) => {
      try {
        fn(...args);
      } catch (err) {
        console.error(err);
      }
    });
  }
}

const itemRedo = new GlobalListener();

const TableItem = memo(({ d }) => {
  const [sel, setSel] = useState(false);
  useEffect(() => {
    itemRedo.listen(() => {
      setSel(false);
    });
  }, []);
  return (
    <tr
      //layout="position"
      className={
        "transition-colors *:transition-colors last:*:first:rounded-bl-2xl last:*:last:rounded-br-2xl " +
        (sel
          ? "*:bg-zinc-300/15 lg:hover:*:bg-zinc-300/20"
          : " lg:hover:*:bg-zinc-300/15")
      }
      onClick={() => {
        setSel(!sel);
      }}
    >
      <td>{d["num"]}</td>
      <td>
        {d.subject
          .toString()
          .split("\n")
          .map((line, i, array) => (
            <Fragment key={i}>
              {line}
              {i < array.length - 1 && <br />}
            </Fragment>
          )) || d.subject}
      </td>
      <td>
        {d.cabinet
          .toString()
          .split("\n")
          .map((line, i, array) => (
            <Fragment key={i}>
              {line}
              {i < array.length - 1 && <br />}
            </Fragment>
          )) || d.cabinet}
      </td>
      <td>
        <div className="flex items-center justify-center min-h-9">
          <div className="flex gap-0.5 flex-col *:block w-[70%] *:font-light">
            <span className="text-left">{d["t-begin"]}&nbsp;&nbsp;-</span>
            <span className="text-right">{d["t-end"]}</span>
          </div>
        </div>
      </td>
    </tr>
  );
});
