import toast from "react-hot-toast";
import { useSnapshot } from "valtio";
import { ArrowSquareOut, Copy, Trash, Wallet, X } from "phosphor-react";
import React, { useEffect, useState, FC } from "react";
import Dinero from "dinero.js";

import Button from "./Button";
import { addFaucetAccount, importAccount } from "../state/actions";
import state from "../state";
import Box from "./Box";
import { Container, Heading, Stack, Text, Flex } from ".";
import {
  Dialog,
  DialogContent,
  DialogTitle,
  DialogDescription,
  DialogClose,
  DialogTrigger,
} from "./Dialog";
import { css } from "../stitches.config";
import { Input, Label } from "./Input";
import truncate from "../utils/truncate";

const labelStyle = css({
  color: "$mauve10",
  textTransform: "uppercase",
  fontSize: "10px",
  mb: "$0.5",
});
import transactionsData from "../content/transactions.json";
import { SetHookDialog } from "./SetHookDialog";
import { addFunds } from "../state/actions/addFaucetAccount";
import { deleteHook } from "../state/actions/deployHook";

export const AccountDialog = ({
  activeAccountAddress,
  setActiveAccountAddress,
}: {
  activeAccountAddress: string | null;
  setActiveAccountAddress: React.Dispatch<React.SetStateAction<string | null>>;
}) => {
  const snap = useSnapshot(state);
  const [showSecret, setShowSecret] = useState(false);
  const activeAccount = snap.accounts.find(
    (account) => account.address === activeAccountAddress
  );
  return (
    <Dialog
      open={Boolean(activeAccountAddress)}
      onOpenChange={(open) => {
        setShowSecret(false);
        !open && setActiveAccountAddress(null);
      }}
    >
      <DialogContent
        css={{
          backgroundColor: "$mauve1 !important",
          border: "1px solid $mauve2",
          ".dark &": {
            // backgroundColor: "$black !important",
          },
          p: "$3",
          "&:before": {
            content: " ",
            position: "absolute",
            top: 0,
            right: 0,
            bottom: 0,
            left: 0,
            opacity: 0.2,
            ".dark &": {
              opacity: 1,
            },
            zIndex: 0,
            pointerEvents: "none",
            backgroundImage: `url('/pattern-dark.svg'), url('/pattern-dark-2.svg')`,
            backgroundRepeat: "no-repeat",
            backgroundPosition: "bottom left, top right",
          },
        }}
      >
        <DialogTitle
          css={{
            display: "flex",
            width: "100%",
            alignItems: "center",
            borderBottom: "1px solid $mauve6",
            pb: "$3",
            gap: "$3",
            fontSize: "$md",
          }}
        >
          <Wallet size="15px" /> {activeAccount?.name}
          <DialogClose asChild>
            <Button
              size="xs"
              outline
              css={{ ml: "auto", mr: "$9" }}
              tabIndex={-1}
              onClick={() => {
                const index = state.accounts.findIndex(
                  (acc) => acc.address === activeAccount?.address
                );
                state.accounts.splice(index, 1);
              }}
            >
              Delete Account <Trash size="15px" />
            </Button>
          </DialogClose>
        </DialogTitle>
        <DialogDescription as="div" css={{ fontFamily: "$monospace" }}>
          <Stack css={{ display: "flex", flexDirection: "column", gap: "$3" }}>
            <Flex css={{ alignItems: "center" }}>
              <Flex css={{ flexDirection: "column" }}>
                <Text className={labelStyle()}>Account Address</Text>
                <Text
                  css={{
                    fontFamily: "$monospace",
                  }}
                >
                  {activeAccount?.address}
                </Text>
              </Flex>
              <Flex css={{ marginLeft: "auto", color: "$mauve12" }}>
                <Button
                  size="sm"
                  ghost
                  css={{ mt: "$3" }}
                  onClick={() => {
                    navigator.clipboard.writeText(activeAccount?.address || "");
                    toast.success("Copied address to clipboard");
                  }}
                >
                  <Copy size="15px" />
                </Button>
              </Flex>
            </Flex>
            <Flex css={{ alignItems: "center" }}>
              <Flex css={{ flexDirection: "column" }}>
                <Text className={labelStyle()}>Secret</Text>
                <Text
                  as="div"
                  css={{
                    fontFamily: "$monospace",
                    display: "flex",
                    alignItems: "center",
                  }}
                >
                  {showSecret
                    ? activeAccount?.secret
                    : "•".repeat(activeAccount?.secret.length || 16)}{" "}
                  <Button
                    css={{
                      fontFamily: "$monospace",
                      lineHeight: 2,
                      mt: "2px",
                      ml: "$3",
                    }}
                    ghost
                    size="xs"
                    onClick={() => setShowSecret((curr) => !curr)}
                  >
                    {showSecret ? "Hide" : "Show"}
                  </Button>
                </Text>
              </Flex>
              <Flex css={{ marginLeft: "auto", color: "$mauve12" }}>
                <Button
                  size="sm"
                  ghost
                  onClick={() => {
                    navigator.clipboard.writeText(activeAccount?.secret || "");
                    toast.success("Copied secret to clipboard");
                  }}
                  css={{ mt: "$3" }}
                >
                  <Copy size="15px" />
                </Button>
              </Flex>
            </Flex>
            <Flex css={{ alignItems: "center" }}>
              <Flex css={{ flexDirection: "column" }}>
                <Text className={labelStyle()}>Balances & Objects</Text>
                <Text
                  css={{
                    fontFamily: "$monospace",
                    display: "flex",
                    alignItems: "center",
                  }}
                >
                  {Dinero({
                    amount: Number(activeAccount?.xrp || "0"),
                    precision: 6,
                  })
                    .toUnit()
                    .toLocaleString(undefined, {
                      style: "currency",
                      currency: "XRP",
                      currencyDisplay: "name",
                    })}
                  <Button
                    css={{
                      fontFamily: "$monospace",
                      lineHeight: 2,
                      mt: "2px",
                      ml: "$3",
                    }}
                    ghost
                    size="xs"
                    onClick={() => {
                      addFunds(activeAccount?.address || "");
                    }}
                  >
                    Add Funds
                  </Button>
                </Text>
              </Flex>
              <Flex css={{ marginLeft: "auto" }}>
                <a
                  href={`https://${process.env.NEXT_PUBLIC_EXPLORER_URL}/${activeAccount?.address}`}
                  target="_blank"
                  rel="noreferrer noopener"
                >
                  <Button
                    size="sm"
                    ghost
                    css={{ color: "$grass11 !important", mt: "$3" }}
                  >
                    <ArrowSquareOut size="15px" />
                  </Button>
                </a>
              </Flex>
            </Flex>
            <Flex css={{ alignItems: "center" }}>
              <Flex css={{ flexDirection: "column" }}>
                <Text className={labelStyle()}>Installed Hooks</Text>
                <Text
                  css={{
                    fontFamily: "$monospace",
                  }}
                >
                  {activeAccount && activeAccount.hooks.length > 0
                    ? activeAccount.hooks.map((i) => truncate(i, 12)).join(",")
                    : "–"}
                </Text>
              </Flex>
              {activeAccount && activeAccount?.hooks?.length > 0 && (
                <Flex css={{ marginLeft: "auto" }}>
                  <Button
                    size="xs"
                    outline
                    disabled={activeAccount.isLoading}
                    css={{ mt: "$3", mr: "$1", ml: "auto" }}
                    onClick={() => {
                      deleteHook(activeAccount);
                    }}
                  >
                    Delete Hook <Trash size="15px" />
                  </Button>
                </Flex>
              )}
            </Flex>
          </Stack>
        </DialogDescription>
        <DialogClose asChild>
          <Box css={{ position: "absolute", top: "$3", right: "$3" }}>
            <X size="20px" />
          </Box>
        </DialogClose>
      </DialogContent>
    </Dialog>
  );
};

interface AccountProps {
  card?: boolean;
  hideDeployBtn?: boolean;
  showHookStats?: boolean;
}

const Accounts: FC<AccountProps> = (props) => {
  const snap = useSnapshot(state);
  const [activeAccountAddress, setActiveAccountAddress] = useState<
    string | null
  >(null);
  useEffect(() => {
    const fetchAccInfo = async () => {
      if (snap.clientStatus === "online") {
        const requests = snap.accounts.map((acc) =>
          snap.client?.send({
            id: `hooks-builder-req-info-${acc.address}`,
            command: "account_info",
            account: acc.address,
          })
        );
        const responses = await Promise.all(requests);
        responses.forEach((res: any) => {
          const address = res?.account_data?.Account as string;
          const balance = res?.account_data?.Balance as string;
          const sequence = res?.account_data?.Sequence as number;
          const accountToUpdate = state.accounts.find(
            (acc) => acc.address === address
          );
          if (accountToUpdate) {
            accountToUpdate.xrp = balance;
            accountToUpdate.sequence = sequence;
            accountToUpdate.error = null;
          } else {
            const oldAccount = state.accounts.find(
              (acc) => acc.address === res?.account
            );
            if (oldAccount) {
              oldAccount.xrp = "0";
              oldAccount.error = {
                code: res?.error,
                message: res?.error_message,
              };
            }
          }
        });
        const objectRequests = snap.accounts.map((acc) => {
          return snap.client?.send({
            id: `hooks-builder-req-objects-${acc.address}`,
            command: "account_objects",
            account: acc.address,
          });
        });
        const objectResponses = await Promise.all(objectRequests);
        objectResponses.forEach((res: any) => {
          const address = res?.account as string;
          const accountToUpdate = state.accounts.find(
            (acc) => acc.address === address
          );
          if (accountToUpdate) {
            accountToUpdate.hooks =
              res.account_objects
                .find((ac: any) => ac?.LedgerEntryType === "Hook")
                ?.Hooks?.map((oo: any) => oo.Hook.HookHash) || [];
          }
        });
      }
    };

    let fetchAccountInfoInterval: NodeJS.Timer;
    if (snap.clientStatus === "online") {
      fetchAccInfo();
      fetchAccountInfoInterval = setInterval(() => fetchAccInfo(), 10000);
    }

    return () => {
      if (snap.accounts.length > 0) {
        if (fetchAccountInfoInterval) {
          clearInterval(fetchAccountInfoInterval);
        }
      }
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [snap.accounts.length, snap.clientStatus]);
  return (
    <Box
      as="div"
      css={{
        display: "flex",
        backgroundColor: props.card ? "$deep" : "$mauve1",
        position: "relative",
        flex: "1",
        height: "100%",
        border: "1px solid $mauve6",
        borderRadius: props.card ? "$md" : undefined,
      }}
    >
      <Container css={{ p: 0, flexShrink: 1, height: "100%" }}>
        <Flex
          css={{
            py: "$3",
            borderBottom: props.card ? "1px solid $mauve6" : undefined,
          }}
        >
          <Heading
            as="h3"
            css={{
              fontWeight: 300,
              m: 0,
              fontSize: "11px",
              color: "$mauve12",
              px: "$3",
              textTransform: "uppercase",
              alignItems: "center",
              display: "inline-flex",
              gap: "$3",
            }}
          >
            <Wallet size="15px" /> <Text css={{ lineHeight: 1 }}>Accounts</Text>
          </Heading>
          <Flex css={{ ml: "auto", gap: "$3", marginRight: "$3" }}>
            <Button ghost size="sm" onClick={() => addFaucetAccount(true)}>
              Create
            </Button>
            <ImportAccountDialog />
          </Flex>
        </Flex>
        <Stack
          css={{
            flexDirection: "column",
            width: "100%",
            fontSize: "13px",
            wordWrap: "break-word",
            fontWeight: "$body",
            gap: 0,
            height: "calc(100% - 52px)",
            flexWrap: "nowrap",
            overflowY: "auto",
          }}
        >
          {snap.accounts.map((account) => (
            <Flex
              column
              key={account.address + account.name}
              onClick={() => setActiveAccountAddress(account.address)}
              css={{
                px: "$3",
                py: props.card ? "$3" : "$2",
                cursor: "pointer",
                borderBottom: props.card ? "1px solid $mauve6" : undefined,
                "@hover": {
                  "&:hover": {
                    background: "$backgroundAlt",
                  },
                },
              }}
            >
              <Flex
                row
                css={{
                  justifyContent: "space-between",
                }}
              >
                <Box>
                  <Text>{account.name} </Text>
                  <Text
                    css={{
                      color: "$textMuted",
                      wordBreak: "break-word",
                    }}
                  >
                    {account.address}{" "}
                    {!account?.error ? (
                      `(${Dinero({
                        amount: Number(account?.xrp || "0"),
                        precision: 6,
                      })
                        .toUnit()
                        .toLocaleString(undefined, {
                          style: "currency",
                          currency: "XRP",
                          currencyDisplay: "name",
                        })})`
                    ) : (
                      <Box css={{ color: "$red11" }}>
                        (Account not found, request funds to activate account)
                      </Box>
                    )}
                  </Text>
                </Box>
                {!props.hideDeployBtn && (
                  <div
                    className="hook-deploy-button"
                    onClick={(e) => {
                      e.stopPropagation();
                    }}
                  >
                    <SetHookDialog accountAddress={account.address} />
                  </div>
                )}
              </Flex>
              {props.showHookStats && (
                <Text muted small css={{ mt: "$2" }}>
                  {account.hooks.length} hook
                  {account.hooks.length === 1 ? "" : "s"} installed
                </Text>
              )}
            </Flex>
          ))}
        </Stack>
      </Container>
      <AccountDialog
        activeAccountAddress={activeAccountAddress}
        setActiveAccountAddress={setActiveAccountAddress}
      />
    </Box>
  );
};

export const transactionsOptions = transactionsData.map((tx) => ({
  value: tx.TransactionType,
  label: tx.TransactionType,
}));

const ImportAccountDialog = () => {
  const [value, setValue] = useState("");
  return (
    <Dialog>
      <DialogTrigger asChild>
        <Button ghost size="sm">
          Import
        </Button>
      </DialogTrigger>
      <DialogContent>
        <DialogTitle>Import account</DialogTitle>
        <DialogDescription>
          <Label>Add account secret</Label>
          <Input
            name="secret"
            type="password"
            value={value}
            onChange={(e) => setValue(e.target.value)}
          />
        </DialogDescription>

        <Flex
          css={{
            marginTop: 25,
            justifyContent: "flex-end",
            gap: "$3",
          }}
        >
          <DialogClose asChild>
            <Button outline>Cancel</Button>
          </DialogClose>
          <DialogClose asChild>
            <Button
              variant="primary"
              onClick={() => {
                importAccount(value);
                setValue("");
              }}
            >
              Import account
            </Button>
          </DialogClose>
        </Flex>
        <DialogClose asChild>
          <Box css={{ position: "absolute", top: "$3", right: "$3" }}>
            <X size="20px" />
          </Box>
        </DialogClose>
      </DialogContent>
    </Dialog>
  );
};

export default Accounts;
