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 { type FormEvent, useState } from "react";

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

  if (![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,
    });

  const now = new Date();
  const members = etData.results as { [k: string]: any }[];
  const currentMonthEvents = await context.env.D1.prepare(
    "SELECT answered_at, created_by, performed_at, reached_minimum_player_count, type FROM events WHERE year = ? AND month = ?;",
  )
    .bind(now.getUTCFullYear(), now.getUTCMonth() + 1)
    .all();

  if (!currentMonthEvents.error) {
    for (const event of currentMonthEvents.results as { [k: string]: any }[]) {
      const memberIdx = members.findIndex((m) => m.id === event.created_by);

      if (memberIdx === -1) continue;

      if (event.performed_at) members[memberIdx].points += 10;
      if (event.type === "gamenight" && event.reached_minimum_player_count)
        members[memberIdx].points += 10;
      if (
        event.type === "rotw" &&
        event.answered_at - event.performed_at >= 86400000
      )
        members[memberIdx].points += 10;
      if (!event.performed_at && event.day < now.getUTCDate())
        members[memberIdx].points -= 5;
    }
  }

  return { members } as {
    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 {
    isOpen: isNameChangeOpen,
    onClose: closeNameChange,
    onOpen: openNameChange,
  } = useDisclosure();
  const {
    isOpen: isChangeRobloxOpen,
    onClose: closeChangeRoblox,
    onOpen: openChangeRoblox,
  } = useDisclosure();

  function validateRobloxName(e: FormEvent<HTMLInputElement>) {
    const data = (e.target as HTMLInputElement).value as string;

    if (!data) return;

    if (
      data.match(/\W/) ||
      data.length > 20 ||
      // Need Number pseudo-constructor since matches might be null
      (data.match(/_/g)?.length || 0) > 1 ||
      data.startsWith("_")
    )
      e.preventDefault();
  }

  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={isChangeRobloxOpen} onClose={closeChangeRoblox}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Change Roblox User</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Heading mb="8px" size="xs">
              New Roblox Username
            </Heading>
            <Input
              maxLength={20}
              onBeforeInput={validateRobloxName}
              onChange={(e) => setAddingMemberRoblox(e.target.value)}
              placeholder="builderman"
            />
          </ModalBody>
          <ModalFooter>
            <Button
              onClick={() => {
                setAddingMemberRoblox("");
                closeChangeRoblox();
              }}
            >
              Cancel
            </Button>
            <Button
              colorScheme="blue"
              ml="8px"
              onClick={async () => {
                const changeResp = await fetch(
                  "/api/events-team/team-members/user",
                  {
                    body: JSON.stringify({
                      id: currentModalMember,
                      roblox_username: addingMemberRoblox,
                    }),
                    headers: {
                      "content-type": "application/json",
                    },
                    method: "PATCH",
                  },
                );

                if (!changeResp.ok) {
                  let errorMsg = "Unknown error";

                  try {
                    errorMsg = ((await changeResp.json()) as { error: string })
                      .error;
                  } catch {}

                  toast({
                    description: errorMsg,
                    status: "error",
                    title: "Failed to change",
                  });

                  return;
                }

                toast({
                  description: "Roblox information updated",
                  status: "success",
                  title: "Change successful",
                });

                const newMemberData = memberData;
                newMemberData[
                  memberData.findIndex((m) => m.id === currentModalMember)
                ].roblox_id = (
                  (await changeResp.json()) as {
                    name: string;
                    roblox_id: number;
                  }
                ).roblox_id;

                setMemberData([...newMemberData]);
                closeChangeRoblox();
                setModalMember("");
                setAddingMemberRoblox("");
              }}
            >
              Change
            </Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
      <Modal isOpen={isNameChangeOpen} onClose={closeNameChange}>
        <ModalOverlay />
        <ModalContent>
          <ModalHeader>Change Name</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <Input
              maxLength={64}
              onChange={(e) => setAddingMemberName(e.target.value)}
              placeholder="New name"
            />
          </ModalBody>
          <ModalFooter>
            <Button
              onClick={() => {
                setAddingMemberName("");
                closeNameChange();
              }}
            >
              Cancel
            </Button>
            <Button
              colorScheme="blue"
              ml="8px"
              onClick={async () => {
                const nameUpdateResp = await fetch(
                  "/api/events-team/team-members/user",
                  {
                    body: JSON.stringify({
                      id: currentModalMember,
                      name: addingMemberName,
                    }),
                    headers: {
                      "content-type": "application/json",
                    },
                    method: "PATCH",
                  },
                );

                const newName = addingMemberName;

                closeNameChange();
                setAddingMemberName("");

                if (!nameUpdateResp.ok) {
                  let errorMsg = "Unknown error";

                  try {
                    errorMsg = (
                      (await nameUpdateResp.json()) as { error: string }
                    ).error;
                  } catch {}

                  toast({
                    description: errorMsg,
                    status: "error",
                    title: "Error",
                  });

                  return;
                }

                toast({
                  description: `Name changed to ${newName}`,
                  status: "success",
                  title: "Name changed",
                });

                const newMemberData = memberData;
                newMemberData[
                  memberData.findIndex((m) => m.id === currentModalMember)
                ].name = newName;

                setMemberData([...newMemberData]);
              }}
            >
              Update Name
            </Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
      <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>
          <ModalFooter>
            <Button
              onClick={() => {
                setRealtimePoints(0);
                onClose();
              }}
            >
              Cancel
            </Button>
            <Button
              colorScheme="blue"
              ml="8px"
              onClick={async () =>
                await updatePoints(currentModalMember, realtimePoints)
              }
            >
              Update Points
            </Button>
          </ModalFooter>
        </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={validateRobloxName}
              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>
            Click/tap on a user's points count to change their points, their
            user id to see and manage strikes.
          </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>
                  <Link href={`/et-members/strikes/${member.id}`}>
                    {member.id}
                  </Link>
                </Td>
                <Td>
                  <Link
                    onClick={() => {
                      setModalMember(member.id);
                      openNameChange();
                    }}
                  >
                    {member.name}
                  </Link>
                </Td>
                <Td>
                  <Link
                    onClick={() => {
                      setModalMember(member.id);
                      openChangeRoblox();
                    }}
                  >
                    {member.roblox_id}
                  </Link>
                </Td>
                <Td>
                  <Link
                    onClick={() => {
                      setModalMember(member.id);
                      onOpen();
                    }}
                  >
                    {member.points}
                  </Link>
                </Td>
                <Td>
                  <Link
                    onClick={() => {
                      setDelMember({ id: member.id, name: member.name });
                      openDelConfirm();
                    }}
                  >
                    Remove
                  </Link>
                </Td>
              </Tr>
            ))}
          </Tbody>
        </Table>
      </TableContainer>
      <Link color="#646cff" onClick={openAddMember} mt="16px">
        Add Member
      </Link>
    </Container>
  );
}