import {
  Box,
  Button,
  Container,
  Flex,
  Heading,
  HStack,
  Popover,
  PopoverArrow,
  PopoverBody,
  PopoverCloseButton,
  PopoverContent,
  PopoverHeader,
  PopoverTrigger,
  Select,
  Spacer,
  useBreakpointValue,
  useDisclosure,
  useToast,
  VStack,
} from "@chakra-ui/react";
import {
  type MutableRefObject,
  type ReactNode,
  useEffect,
  useRef,
  useState,
} from "react";
import { useLoaderData } from "@remix-run/react";
import AppealBans from "../../components/AppealBans.js";
import AppealCard from "../../components/AppealCard.js";
import GameAppealCard from "../../components/GameAppealCard.js";
import GameModManagementModal from "../../components/GameModManagementModal.js";
import NewGameBan from "../../components/NewGameBan.js";
import NewInfractionModal from "../../components/NewInfractionModal.js";
import ReportCard from "../../components/ReportCard.js";
import NewInactivityNotice from "../../components/NewInactivityNotice.js";
import InactivityNoticeCard from "../../components/InactivityNoticeCard.js";

export async function loader({ context }: { context: RequestContext }) {
  const { current_user: currentUser } = context.data;

  if (!currentUser)
    throw new Response(null, {
      status: 401,
    });

  const departments = {
    DM: 1 << 2,
    ET: 1 << 3,
    FM: 1 << 10,
    WM: 1 << 9,
  };

  const newItemPermissions = {
    active_inactivities: [1 << 0, 1 << 2, 1 << 3, 1 << 9, 1 << 10],
    appeal_bans: [1 << 0, 1 << 11],
    game_ban: [1 << 5],
    inactivity: [1 << 2, 1 << 3, 1 << 9, 1 << 10],
    infraction: [1 << 0, 1 << 2, 1 << 6, 1 << 7],
    user_lookup: [1 << 5, 1 << 8],
  };

  const newItemNames: { [k: string]: string } = {
    active_inactivities: "Active Inactivity Notices",
    appeal_bans: "Appeal Bans",
    game_ban: "New Game Ban",
    gme: "Game Mod Management",
    inactivity: "New Inactivity Notice",
    infraction: "New Infraction",
    user_lookup: "User Lookup",
  };

  const typePermissions = {
    appeal: [1 << 0, 1 << 11],
    gma: [1 << 5],
    inactivity: [1 << 4, 1 << 6, 1 << 7, 1 << 11, 1 << 12],
    report: [1 << 5],
  };

  const typeNames: { [k: string]: string } = {
    appeal: "Discord Appeals",
    gma: "Game Appeals",
    inactivity: "Inactivity Notices",
    report: "Game Reports",
  };

  const can_edit_ban_users = [
    "165594923586945025",
    "289372404541554689",
    "396347223736057866",
  ].includes(currentUser.id);

  const allowedNewItems = [];
  const allowedTypes = [];

  for (const [item, ints] of Object.entries(newItemPermissions)) {
    if (ints.find((i) => currentUser.permissions & i))
      allowedNewItems.push({ name: newItemNames[item], value: item });
  }

  for (const [type, ints] of Object.entries(typePermissions)) {
    if (ints.find((i) => currentUser.permissions & i))
      allowedTypes.push({ name: typeNames[type], value: type });
  }

  if (can_edit_ban_users)
    allowedNewItems.push({ name: "Game Mod Management", value: "gme" });

  if (!allowedTypes.length && !allowedNewItems.length)
    throw new Response(null, {
      status: 403,
    });

  return {
    can_edit_ban_users,
    departments: Object.entries(departments)
      .filter((d) => d[1] & currentUser.permissions)
      .map((arr) => arr[0]),
    entry_types: allowedTypes,
    item_types: allowedNewItems,
  };
}

export function meta() {
  return [
    {
      title: "Moderation Queue - Car Crushers",
    },
  ];
}

export default function () {
  const pageProps = useLoaderData<typeof loader>();
  const isDesktop = useBreakpointValue({ base: false, lg: true });
  const entryTypes = [];
  const [entries, setEntries] = useState(
    [] as { element: ReactNode; id: string }[],
  );
  const [before, setBefore] = useState(Date.now());
  const [queue, setQueue] = useState("");
  const messageChannel: MutableRefObject<MessageChannel | null> = useRef(null);
  const toast = useToast();

  for (const type of pageProps.entry_types)
    entryTypes.push(
      <option key={type.value} value={type.value}>
        {type.name}
      </option>,
    );

  useEffect(() => {
    if (messageChannel.current) {
      messageChannel.current.port1.onmessage = function (ev) {
        const { data }: { data: string } = ev;

        setEntries([...entries].filter((entry) => entry.id !== data));
      };
    }
  }, [entries, messageChannel.current]);

  async function updateQueue(
    queue_type: string,
    before: number,
    show_closed = false,
    jump_item_to_top = false,
    clear_all_others = false,
  ): Promise<void> {
    const searchParams = new URLSearchParams(location.search);
    const itemId = searchParams.get("id");
    const queueType = searchParams.get("type") ?? queue_type;

    if (!pageProps.entry_types.find((type) => type.value === queueType)) {
      toast({
        description: "You cannot access that queue",
        isClosable: true,
        status: "error",
        title: "Forbidden",
      });

      return;
    }

    if (!searchParams.get("type") && itemId) {
      toast({
        description: "Cannot load item by id without type",
        isClosable: true,
        status: "error",
        title: "Bad link",
      });

      return;
    }

    if (queueType !== queue_type) setQueue(queueType);

    let queueReq: Response;

    try {
      queueReq = await fetch(
        `/api/mod-queue/${queueType}/list?before=${before}&showClosed=${show_closed}`,
      );
    } catch {
      alert("Failed to load mod queue");
      return;
    }

    if (!queueReq.ok) {
      const errorData: { error: string } = await queueReq.json();

      toast({
        description: errorData.error,
        duration: 10000,
        isClosable: true,
        status: "error",
        title: "Failed to load queue",
      });

      return;
    }

    let entryData: { [k: string]: any }[] = await queueReq.json();
    const newEntries = clear_all_others ? [] : [...entries];

    if (itemId && jump_item_to_top) {
      history.replaceState(null, "", location.origin + location.pathname);

      const specifiedItem = entryData.find((e) => e.id === itemId);

      if (specifiedItem) {
        entryData = entryData.filter((entry) => entry.id !== specifiedItem.id);
        entryData.unshift(specifiedItem);
      } else {
        const itemReq = await fetch(`/api/mod-queue/${queueType}/${itemId}`);

        if (!itemReq.ok) {
          toast({
            description: "Failed to load item with id " + itemId,
            duration: 10000,
            isClosable: true,
            status: "error",
            title: ((await itemReq.json()) as { error: string }).error,
          });
        } else {
          const itemData: { [k: string]: any } = await itemReq.json();

          entryData.unshift(itemData);
        }
      }
    }

    if (!entryData.length) {
      setEntries([]);
      return;
    }

    for (const entry of entryData) {
      let cardType = queueType;

      if (
        entryData.indexOf(entry) > 0 &&
        entryData.filter((d) => d.id === entry.id).length > 1
      )
        continue;

      switch (cardType) {
        case "appeal":
          newEntries.push({
            element: (
              <AppealCard
                {...(entry as AppealCardProps & { port?: MessagePort })}
                port={messageChannel.current?.port2}
              />
            ),
            id: `appeal_${entry.id}`,
          });

          break;

        case "gma":
          newEntries.push({
            element: (
              <GameAppealCard
                {...(entry as GameAppealProps & { port?: MessagePort })}
                port={messageChannel.current?.port2}
              />
            ),
            id: `gma_${entry.id}`,
          });

          break;

        case "inactivity":
          newEntries.push({
            element: (
              <InactivityNoticeCard
                {...(entry as InactivityNoticeProps & { port?: MessagePort })}
                port={messageChannel.current?.port2}
              />
            ),
            id: `inactivity_${entry.id}`,
          });

          break;

        case "report":
          newEntries.push({
            element: (
              <ReportCard
                {...(entry as ReportCardProps & { port?: MessagePort })}
                port={messageChannel.current?.port2}
              />
            ),
            id: `report_${entry.id}`,
          });

          break;
      }
    }

    setEntries(newEntries);
    setBefore(entryData[entryData.length - 1].created_at);
  }

  const itemModals: {
    [k: string]: {
      isOpen: boolean;
      onOpen: () => void;
      onClose: () => void;
      [k: string]: any;
    };
  } = {
    active_inactivities: {
      isOpen: false,
      onClose: () => {},
      onOpen: () => location.assign("/inactivities"),
    },
    appeal_bans: useDisclosure(),
    game_ban: useDisclosure(),
    gme: useDisclosure(),
    inactivity: useDisclosure(),
    infraction: useDisclosure(),
    user_lookup: {
      isOpen: false,
      onClose: () => {},
      onOpen: () => location.assign("/hammer"),
    },
  };

  useEffect(() => {
    messageChannel.current = new MessageChannel();

    (async function () {
      if (!pageProps.entry_types.length) return;
      await updateQueue(pageProps.entry_types[0].value, before, false, true);
    })();

    const searchParams = new URLSearchParams(location.search);
    const modal = searchParams.get("modal");

    if (!modal || !pageProps.item_types.find((m) => m.value === modal)) return;

    itemModals[modal].onOpen();
  }, []);

  const ItemDisplay = (
    <Select
      onChange={async (v) => {
        setBefore(Date.now());

        const { target } = v;

        setQueue(target.options[target.selectedIndex].value);

        await updateQueue(
          target.options[target.selectedIndex].value,
          Date.now(),
          false,
          false,
          true,
        );
      }}
      value={queue}
    >
      {entryTypes}
    </Select>
  );

  const ToolsContent = (
    <Popover placement="bottom-end">
      <PopoverTrigger>
        <Button>
          <svg
            xmlns="http://www.w3.org/2000/svg"
            width="16"
            height="16"
            fill="currentColor"
            viewBox="0 0 16 16"
          >
            <path
              fillRule="evenodd"
              d="M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z"
            />
          </svg>
        </Button>
      </PopoverTrigger>
      <PopoverContent>
        <PopoverArrow />
        <PopoverCloseButton />
        <PopoverHeader>Tools</PopoverHeader>
        <PopoverBody>
          <VStack>
            {pageProps.item_types.map((item) => (
              <Button
                key={item.value}
                onClick={() => itemModals[item.value].onOpen()}
                w="100%"
              >
                {item.name}
              </Button>
            ))}
          </VStack>
        </PopoverBody>
      </PopoverContent>
    </Popover>
  );

  return (
    <Container maxW="container.lg">
      <AppealBans
        isOpen={itemModals.appeal_bans.isOpen}
        onClose={itemModals.appeal_bans.onClose}
      />
      <GameModManagementModal
        isOpen={itemModals.gme.isOpen}
        onClose={itemModals.gme.onClose}
      />
      <NewGameBan
        isOpen={itemModals.game_ban.isOpen}
        onClose={itemModals.game_ban.onClose}
      />
      <NewInactivityNotice
        departments={pageProps.departments}
        isOpen={itemModals.inactivity.isOpen}
        onClose={itemModals.inactivity.onClose}
      />
      <NewInfractionModal
        isOpen={itemModals.infraction.isOpen}
        onClose={itemModals.infraction.onClose}
      />
      <Flex>
        <VStack w={isDesktop ? "container.md" : "container.lg"}>
          <Box display={isDesktop ? "none" : undefined} mb="16px" w="90%">
            <HStack>
              {ItemDisplay}
              {ToolsContent}
            </HStack>
          </Box>
          {entries.length ? (
            entries.map((entry) => entry.element)
          ) : (
            <Container
              left="50%"
              maxW="container.md"
              pos="absolute"
              mt="64px"
              transform="translate(-50%)"
            >
              <Flex>
                <Spacer />
                <img alt="Thonkery" src="/files/Thonkery.png" />
                <Spacer />
              </Flex>
              <br />
              <Heading textAlign="center">Nothing here</Heading>
            </Container>
          )}
        </VStack>
        <Box display={isDesktop ? undefined : "none"} ml="16px" w="248px">
          <HStack>
            {ItemDisplay}
            {ToolsContent}
          </HStack>
        </Box>
      </Flex>
    </Container>
  );
}