import { disposeTempAliasRequest, fetchOwnAliases, requestTemporaryAlias, claimTemporaryAlias, deleteAlias } from "@/lib/actions";
import { GRAVATAR_DEFAULT } from "@/lib/constants";
import { AliasEntry, AliasRequestEntry } from "@/lib/db";
import { ToastContext } from "@/lib/providers/ToastProvider";
import { sha256sum } from "@/lib/util";
import { Avatar, Badge, Box, Button, Card, Code, Dialog, Flex, Heading, IconButton, ScrollArea, Select, Table, Text, TextField } from "@radix-ui/themes";
import { CircleUserIcon, CopyIcon, RefreshCcwIcon, UsersRoundIcon } from "lucide-react";
import { useSession } from "next-auth/react";
import { useContext, useEffect, useState } from "react";
import GhostMessage from "../GhostMessage";
import LoadingSpinner from "../LoadingSpinner";

export default function TempAliasesCard() {
  const session = useSession().data;
  const [aliases, setAliases] = useState<AliasEntry[] | null>(null);
  const [aliasPreview, setAliasPreview] = useState<AliasRequestEntry | undefined>(undefined);
  const [aliasLabel, setAliasLabel] = useState("");
  const [labelAtEnd, setLabelAtEnd] = useState(false);
  const [aliasStyle, setAliasStyle] = useState<"words" | "random">("words");
  const [open, setOpen] = useState(false);
  const [state, setState] = useState<'input' | 'loading' | 'created'>('input');
  const toast = useContext(ToastContext);

  useEffect(() => {
    fetchOwnAliases(true).then(setAliases);
  }, []);

  const refreshAlias = () => {
    if (aliasPreview?.key) disposeTempAliasRequest(aliasPreview.key); // It's fine if this errors

    if (!aliasLabel) {
      setAliasPreview(undefined);
      return;
    }

    requestTemporaryAlias(
      aliasLabel,
      labelAtEnd,
      aliasStyle,
      aliasPreview?.key,
    ).then(setAliasPreview);
  };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  useEffect(refreshAlias, [aliasLabel, aliasStyle, labelAtEnd]);

  return (
    <Card className="h-fit">
      <Flex direction="row" justify="between" mb="2">
        <Heading size="3">Temporary aliases</Heading>

        <Dialog.Root open={open} onOpenChange={(open) => {
          setOpen(open);
          if (!open) {
            setAliasLabel("");
            setLabelAtEnd(false);
            setAliasStyle("words");
            setState('input');
          }
        }}>
          <Dialog.Trigger>
            <Button variant="outline" size="1">Generate alias</Button>
          </Dialog.Trigger>
          <Dialog.Content>
            <Dialog.Title>Generate alias</Dialog.Title>

            {
              state == "input"
                ? <>
                  <Dialog.Description>
                    Temporary aliases can be used to sign up with services without exposing your primary email address.
                    If an alias starts receiving spam or you no longer use the service, you can delete the alias.
                  </Dialog.Description>

                  <Flex direction="row" gap="3" mt="4">
                    <Flex direction="column" gap="3" grow="1">
                      <Text ml="1" size="1" color="gray" mb="-1">Alias label</Text>
                      <TextField.Root>
                        <TextField.Slot>
                          <CircleUserIcon size="16" />
                        </TextField.Slot>
                        <TextField.Input
                          placeholder="Alias label"
                          value={aliasLabel}
                          onChange={(e) => setAliasLabel(e.currentTarget.value.substring(0, 16))}
                        />
                      </TextField.Root>
                    </Flex>

                    <Flex direction="column" gap="3">
                      <Text ml="1" size="1" color="gray" mb="-1">Alias style</Text>
                      <Select.Root value={aliasStyle} onValueChange={(value) => setAliasStyle(value as any)}>
                        <Select.Trigger />
                        <Select.Content>
                          <Select.Group>
                            <Select.Label>Alias style</Select.Label>
                            <Select.Item value="words">Words</Select.Item>
                            <Select.Item value="random">Random</Select.Item>
                          </Select.Group>
                        </Select.Content>
                      </Select.Root>
                    </Flex>

                    <Flex direction="column" gap="3">
                      <Text ml="1" size="1" color="gray" mb="-1">Label position</Text>
                      <Select.Root value={labelAtEnd ? "after" : "before"} onValueChange={(value) => setLabelAtEnd(value == "after")}>
                        <Select.Trigger />
                        <Select.Content>
                          <Select.Group>
                            <Select.Label>Alias style</Select.Label>
                            <Select.Item value="before">Label first</Select.Item>
                            <Select.Item value="after">Label at end</Select.Item>
                          </Select.Group>
                        </Select.Content>
                      </Select.Root>
                    </Flex>
                  </Flex>

                  <Card mt="4">
                    <Flex direction="row" justify="between" align="center">
                      <Flex direction="row" align="center" gap="3">
                        <Avatar
                          size="3"
                          src={`https://gravatar.com/avatar/${sha256sum(aliasPreview?.alias || "")}?d=${GRAVATAR_DEFAULT}`}
                          fallback={"@"}
                        />
                        <Flex direction="column" gap="0">
                          <Text size="3" weight="medium">Generated alias</Text>
                          <Text size="2" weight="light">{aliasPreview?.alias || "Enter a label first"}</Text>
                        </Flex>
                      </Flex>
                      <IconButton onClick={() => refreshAlias()} disabled={!aliasLabel} variant="surface" size="2">
                        <RefreshCcwIcon size="16" />
                      </IconButton>
                    </Flex>
                  </Card>

                  <Flex gap="3" mt="4" justify="end">
                    <Dialog.Close>
                      <Button variant="outline">Close</Button>
                    </Dialog.Close>
                    <Button
                      disabled={!aliasPreview}
                      variant="soft"
                      onClick={async () => {
                        try {
                          setState('loading');
                          const alias = await claimTemporaryAlias(aliasPreview!.key);
                          setAliases([...(aliases ?? []), alias]);
                          setState('created');
                        } catch (e) {
                          setOpen(false);
                          toast({
                            title: "Failed to create alias",
                            description: `${e}`,
                            variant: "error",
                          });
                        }
                      }}
                    >
                      Create
                    </Button>
                  </Flex>
                </>
                : state == "loading"
                  ? <>
                    <GhostMessage icon={<LoadingSpinner />} header="Creating your alias..." />
                  </>
                  : <>
                    <Card mt="4">
                      <Flex direction="row" justify="between" align="center">
                        <Flex direction="row" align="center" gap="3">
                          <Avatar
                            size="3"
                            src={`https://gravatar.com/avatar/${sha256sum(aliasPreview!.alias)}?d=${GRAVATAR_DEFAULT}`}
                            fallback={"@"}
                          />
                          <Flex direction="column" gap="0">
                            <Text size="3" weight="medium">Alias created</Text>
                            <Text size="2" weight="light">{aliasPreview!.alias}</Text>
                          </Flex>
                        </Flex>
                        <IconButton onClick={() => navigator.clipboard.writeText(aliasPreview!.alias)} variant="surface" size="2">
                          <CopyIcon size="16" />
                        </IconButton>
                      </Flex>
                    </Card>
                    <Flex gap="3" mt="4" justify="end">
                      <Dialog.Close>
                        <Button variant="outline">Close</Button>
                      </Dialog.Close>
                    </Flex>
                  </>
            }
          </Dialog.Content>
        </Dialog.Root>
      </Flex>
      <Text weight="light" size="2">Freely generate and dispose of randomized aliases.</Text>

      {
        aliases == null
          ? <GhostMessage icon={<LoadingSpinner />} header="Loading..." />
          : aliases.length
            ? <Box className="h-52">
              <ScrollArea>
                <Table.Root>
                  <Table.Header>
                    <Table.Row>
                      <Table.ColumnHeaderCell justify='start'>Alias</Table.ColumnHeaderCell>
                      <Table.ColumnHeaderCell justify='end'>Manage</Table.ColumnHeaderCell>
                    </Table.Row>
                  </Table.Header>

                  <Table.Body>
                    {
                      aliases.map((alias => <Table.Row key={alias.id}>
                        <Table.Cell justify='start'>
                          {alias.alias}
                        </Table.Cell>
                        <Table.Cell justify='end'>
                          <Dialog.Root>
                            <Dialog.Trigger>
                              <Button variant="soft" size="1" mr="2">Delete</Button>
                            </Dialog.Trigger>

                            <Dialog.Content>
                              <Dialog.Title>Delete alias</Dialog.Title>
                              <Dialog.Description>
                                Are you sure you want to delete the alias <Code className="whitespace-nowrap">{alias.alias}</Code>?
                                You will not be able to recreate it afterwards.
                              </Dialog.Description>

                              <Flex gap="3" mt="4" justify="end">
                                <Dialog.Close>
                                  <Button variant="outline">Cancel</Button>
                                </Dialog.Close>
                                <Dialog.Close>
                                  <Button
                                    variant="soft"
                                    onClick={async () => {
                                      try {
                                        await deleteAlias(alias.alias);
                                        setAliases(aliases.filter((a) => a.id != alias.id));
                                        toast({
                                          title: "Temporary alias deleted",
                                          description: alias.alias,
                                          variant: "success",
                                        });
                                      } catch (e) {
                                        console.error(e);
                                        toast({
                                          title: "Failed to delete alias",
                                          description: `${e}`,
                                          variant: "error",
                                        });
                                      }
                                    }}
                                  >
                                    Delete
                                  </Button>
                                </Dialog.Close>
                              </Flex>
                            </Dialog.Content>
                          </Dialog.Root>
                        </Table.Cell>
                      </Table.Row>))
                    }
                  </Table.Body>
                </Table.Root>
              </ScrollArea>
            </Box>
            : <GhostMessage icon={<UsersRoundIcon />} header="No aliases" message="Generate an alias to begin" />
      }
    </Card>
  );
}
