import { useLoaderData } from "@remix-run/react";
import {
  Button,
  Container,
  Heading,
  Input,
  Link,
  Modal,
  ModalBody,
  ModalCloseButton,
  ModalContent,
  ModalFooter,
  ModalHeader,
  ModalOverlay,
  NumberDecrementStepper,
  NumberIncrementStepper,
  NumberInput,
  NumberInputField,
  NumberInputStepper,
  Table,
  TableCaption,
  TableContainer,
  Tbody,
  Td,
  Text,
  Th,
  Thead,
  Tr,
  useDisclosure,
  useToast,
} from "@chakra-ui/react";
import { FormEvent, useState } from "react";

export async function loader({ context }: { context: RequestContext }) {
  if (!context.data.current_user)
    throw new Response(null, {
      status: 401,
    });

  if (
    ![1 << 3, 1 << 4, 1 << 12].find(
      (p) => context.data.current_user.permissions & p,
    )
  )
    throw new Response(null, {
      status: 403,
    });

  const etData = await context.env.D1.prepare(
    "SELECT id, name, points, roblox_id FROM et_members;",
  ).all();

  if (etData.error)
    throw new Response(null, {
      status: 500,
    });

  return { can_manage: true, members: etData.results } as {
    can_manage: boolean;
    members: { [k: string]: any }[];
  };
}

export default function () {
  const toast = useToast();

  async function removeMember(id: string) {
    const removeResp = await fetch("/api/events-team/team-members/user", {
      body: JSON.stringify({ id }),
      headers: {
        "content-type": "application/json",
      },
      method: "DELETE",
    });

    if (!removeResp.ok) {
      toast({
        description: "Failed to remove member, try again later",
        status: "error",
        title: "Oops",
      });

      return;
    }

    toast({
      description: "The member was removed from the roster",
      status: "success",
      title: "Member Removed",
    });

    setMemberData(memberData.filter((member) => member.id !== id));
  }

  async function addMember() {
    const addResp = await fetch("/api/events-team/team-members/user", {
      body: JSON.stringify({
        id: addingMemberId,
        name: addingMemberName,
        roblox_username: addingMemberRoblox,
      }),
      headers: {
        "content-type": "application/json",
      },
      method: "POST",
    });

    if (!addResp.ok) {
      toast({
        description: "Failed to add member, try again later",
        status: "error",
        title: "Oops",
      });

      return;
    }

    toast({
      description: `Member ${addingMemberName} was added to the roster`,
      status: "success",
      title: "Member Added",
    });

    location.reload();
  }

  const data = useLoaderData<typeof loader>();
  const [realtimePoints, setRealtimePoints] = useState(0);
  const [currentModalMember, setModalMember] = useState("");
  const [currentDelMember, setDelMember] = useState({ id: "", name: "" });
  const [memberData, setMemberData] = useState(data.members);
  const [addingMemberId, setAddingMemberId] = useState("");
  const [addingMemberName, setAddingMemberName] = useState("");
  const [addingMemberRoblox, setAddingMemberRoblox] = useState("");
  const { isOpen, onClose, onOpen } = useDisclosure();
  const {
    isOpen: isDelConfirmOpen,
    onClose: closeDelConfirm,
    onOpen: openDelConfirm,
  } = useDisclosure();
  const {
    isOpen: isAddMemberOpen,
    onClose: closeAddMember,
    onOpen: openAddMember,
  } = useDisclosure();
  const isManagement = data.can_manage;

  async function updatePoints(id: string, points: number) {
    const updateResp = await fetch(`/api/events-team/points/${id}`, {
      body: JSON.stringify({ points }),
      headers: {
        "content-type": "application/json",
      },
      method: "POST",
    });

    if (!updateResp.ok) {
      toast({
        description: "Failed to update points",
        status: "error",
        title: "Oops!",
      });

      return;
    }

    toast({
      description: `Point count changed to ${points}`,
      status: "success",
      title: "Points updated",
    });

    const newMemberData = memberData;
    newMemberData[memberData.findIndex((m) => m.id === id)].points = points;

    setMemberData(newMemberData);
    onClose();
  }

  return (
    <Container maxW="container.lg">
      <Modal
        isOpen={isOpen}
        onClose={() => {
          setRealtimePoints(0);
          onClose();
        }}
      >
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Modify Points</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <NumberInput
              allowMouseWheel
              defaultValue={realtimePoints}
              onChange={(n) => setRealtimePoints(parseInt(n))}
              mt="8px"
            >
              <NumberInputField />
              <NumberInputStepper>
                <NumberIncrementStepper />
                <NumberDecrementStepper />
              </NumberInputStepper>
            </NumberInput>
          </ModalBody>
          {isManagement ? (
            <ModalFooter>
              <Button
                onClick={() => {
                  setRealtimePoints(0);
                  onClose();
                }}
              >
                Cancel
              </Button>
              <Button
                colorScheme="blue"
                onClick={async () =>
                  await updatePoints(currentModalMember, realtimePoints)
                }
              >
                Update Points
              </Button>
            </ModalFooter>
          ) : null}
        </ModalContent>
      </Modal>
      <Modal isOpen={isDelConfirmOpen} onClose={closeDelConfirm}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Remove Member</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Text>
              You are about to remove {currentDelMember.name} from the Events
              Team roster, this will clear all of their data. Are you sure you
              want to do this?
            </Text>
          </ModalBody>
          <ModalFooter>
            <Button
              colorScheme="blue"
              onClick={() => {
                setDelMember({ id: "", name: "" });
                closeDelConfirm();
              }}
            >
              No
            </Button>
            <Button
              colorScheme="red"
              onClick={async () => {
                await removeMember(currentDelMember.id);
                setDelMember({ id: "", name: "" });
                closeDelConfirm();
              }}
              ml="8px"
            >
              Yes, Remove
            </Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
      <Modal isOpen={isAddMemberOpen} onClose={closeAddMember}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Add Member</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Heading size="xs">User ID</Heading>
            <Input
              maxLength={19}
              onBeforeInput={(e) => {
                const {
                  data,
                }: { data?: string } & FormEvent<HTMLInputElement> = e;

                if (data?.match(/\D/)) e.preventDefault();
              }}
              onChange={(e) => setAddingMemberId(e.target.value)}
              mb="16px"
              type="number"
            />
            <Heading size="xs">Name</Heading>
            <Input
              maxLength={64}
              onChange={(e) => setAddingMemberName(e.target.value)}
              mb="16px"
            />
            <Heading size="xs">Roblox Username (optional)</Heading>
            <Input
              maxLength={20}
              onBeforeInput={(e) => {
                const {
                  data,
                }: { data?: string } & FormEvent<HTMLInputElement> = e;

                if (!data) return;

                if (
                  data.match(/\W/) ||
                  data.length > 20 ||
                  (data.match(/_/g)?.length || 0) > 1 ||
                  data.startsWith("_")
                )
                  e.preventDefault();
              }}
              onChange={(e) => setAddingMemberRoblox(e.target.value)}
            />
          </ModalBody>
          <ModalFooter>
            <Button onClick={closeAddMember}>Close</Button>
            <Button
              colorScheme="blue"
              onClick={async () => await addMember()}
              ml="8px"
            >
              Add
            </Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
      <Heading>Events Team Members</Heading>
      <TableContainer mt="16px">
        <Table variant="simple">
          <TableCaption>
            Points are updated at the end of the month
          </TableCaption>
          <Thead>
            <Tr>
              <Th>Discord ID</Th>
              <Th>Name</Th>
              <Th>Roblox ID</Th>
              <Th>Points</Th>
              <Th>Remove</Th>
            </Tr>
          </Thead>
          <Tbody>
            {memberData.map((member) => (
              <Tr>
                <Td>{member.id}</Td>
                <Td>{member.name}</Td>
                <Td>{member.roblox_id}</Td>
                <Td>
                  {isManagement ? (
                    <Link
                      onClick={() => {
                        setModalMember(member.id);
                        onOpen();
                      }}
                    >
                      {member.points}
                    </Link>
                  ) : (
                    member.points
                  )}
                </Td>
                <Td>
                  {isManagement ? (
                    <Link
                      onClick={() => {
                        setDelMember({ id: member.id, name: member.name });
                        openDelConfirm();
                      }}
                    >
                      Remove
                    </Link>
                  ) : null}
                </Td>
              </Tr>
            ))}
          </Tbody>
        </Table>
      </TableContainer>
      {isManagement ? (
        <Link color="#646cff" onClick={openAddMember} mt="16px">
          Add Member
        </Link>
      ) : null}
    </Container>
  );
}