import { useMemo, useState, useCallback, useEffect } from "react";
import { Box, Typography, Link, Checkbox, FormControlLabel, useTheme } from "@mui/material";
import { useNavigate } from 'react-router-dom';
import { getBlockNumber } from "@wagmi/core";
import { useConfig } from "wagmi";
import { ss58Decode } from "@polkadot-labs/hdkd-helpers";
import { toHex } from "@polkadot-api/utils";
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import { CheckBoxOutlineBlank, CheckBoxOutlined } from "@mui/icons-material";

import Metric from "../../components/Metric/Metric";
import Modal from "../../components/Modal/Modal";
import SwapCard from "../../components/Swap/SwapCard";
import Token from "../../components/Token/Token";
import GhostStyledIcon from "../../components/Icon/GhostIcon";
import { PrimaryButton, SecondaryButton } from "../../components/Button";
import { GATEKEEPER_ADDRESSES, EMPTY_ADDRESS } from "../../constants/addresses";
import { GHOST_CONNECT } from "../../constants/ecosystem";

import { useLocalStorage } from "../../hooks/localstorage";
import { useBreakoutModal } from "../../hooks/breakoutModal";
import { useTokenSymbol, useCirculatingSupply } from "../../hooks/tokens";
import { useEpoch, useGatekeeperApy, useGatekeeperAddress } from "../../hooks/staking";
import { useEvmNetwork, useCurrentIndex, useUnstableProvider } from "../../hooks/ghost";
import { formatNumber, shorten } from "../../helpers";
import { prettifySecondsInDays } from "../../helpers/timeUtil";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";

const BreakoutModal = ({ chainId, address }) => {
    const [step, setStep] = useState(0);
    const [receiver, setReceiver] = useState("");
    const [convertedReceiver, setConvertedReceiver] = useState(undefined);

    const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
    const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");

    const evmNetwork = useEvmNetwork({ evmChainId: chainId });

    const {
        isOpened,
        closeModal: closeModalInner,
        isStakingOpened,
        isClaimBondOpened,
        warmupPeriod,
        setActiveTxIndex,
        defaultFunction,
        executableFunction,
        estimatedAmount
    } = useBreakoutModal();

    const incomingFee = useMemo(() => {
        return new DecimalBigNumber(
            evmNetwork ? evmNetwork.incoming_fee : 100000000,
            7
        );
    }, [evmNetwork]);

    const closeModal = () => {
        setActiveTxIndex(-1);
        closeModalPure();
    }

    const closeModalPure = () => {
        setStep(0);
        setReceiver("");
        closeModalInner();
    }

    const header = useMemo(() => {
        if (isStakingOpened && warmupPeriod <= 0) return "Stake Warmed-up"
        if (isClaimBondOpened && warmupPeriod <= 0) return "Bond Warmed-up"
        if (isStakingOpened && warmupPeriod > 0) return "Stake in Warm-up"
        if (isClaimBondOpened && warmupPeriod > 0) return "Bond in Warm-up"
    }, [isStakingOpened, isClaimBondOpened, warmupPeriod]);

    const bridgeNumbers = useMemo(() => {
        const connectedNetworks = Object.keys(GATEKEEPER_ADDRESSES).length;
        const number = 1 + connectedNetworks * 3;
        return `(${number}, ${number})`;
    }, [chainId]);

    useEffect(() => {
        try {
            const [publicKey, prefix] = ss58Decode(receiver);
            if (prefix !== 1995 && prefix !== 1996) {
                throw new Error("bad prefix");
            }
            setConvertedReceiver(toHex(publicKey));
        } catch {
            setConvertedReceiver(undefined);
        }
    }, [receiver]);

    return (
        <Modal
            headerContent={
                <Box display="flex" justifyContent="center" alignItems="center" gap="15px">
                    <Typography variant="h4">{step === 0 ? header : step === 1 ? `Start ${bridgeNumbers} ${"Stake\u00B2"}` : "Bridge Confirmation"}</Typography>
                </Box>
            }
            open={isOpened}
            onClose={closeModal}
            maxWidth="380px"
            minHeight="200px"
        >
            <Box height="420px" display="flex" flexDirection="column" justifyContent="space-between">
                {step === 0
                    ? <WelcomeView
                        isStakingOpened={isStakingOpened}
                        chainId={chainId}
                        warmupPeriod={warmupPeriod}
                        ghstSymbol={ghstSymbol}
                        ftsoSymbol={ftsoSymbol}
                        bridgeNumbers={bridgeNumbers}
                        defaultFunction={defaultFunction}
                        goNext={() => setStep(1)}
                        closeModal={closeModal}
                    />
                    : step === 1
                        ? <BridgeView
                            receiver={receiver}
                            setReceiver={setReceiver}
                            chainId={chainId}
                            bridgeNumbers={bridgeNumbers}
                            ghstSymbol={ghstSymbol}
                            estimatedAmount={estimatedAmount}
                            incomingFee={incomingFee}
                            goNext={() => setStep(2)}
                            convertedReceiver={convertedReceiver}
                            setConvertedReceiver={setConvertedReceiver}

                        />
                        : <ConfirmStep
                            chainId={chainId}
                            address={address}
                            receiver={receiver}
                            executableFunction={executableFunction}
                            isStakingOpened={isStakingOpened}
                            bridgeNumbers={bridgeNumbers}
                            incomingFee={incomingFee}
                            estimatedAmount={estimatedAmount}
                            ghstSymbol={ghstSymbol}
                            setActiveTxIndex={setActiveTxIndex}
                            closeModal={closeModalPure}
                            evmNetwork={evmNetwork}
                            convertedReceiver={convertedReceiver}
                        />
                }
            </Box>
        </Modal>
    )
}

const BridgeView = ({
    chainId,
    receiver,
    setReceiver,
    convertedReceiver,
    setConvertedReceiver,
    bridgeNumbers,
    ghstSymbol,
    estimatedAmount,
    goNext,
    incomingFee
}) => {
    const theme = useTheme();

    const config = useConfig();
    const { gatekeeperAddress } = useGatekeeperAddress(chainId);

    const chainExplorerUrl = useMemo(() => {
        const client = config?.getClient();
        return client?.chain?.blockExplorers?.default?.url;
    }, [config]);

    return (
        <>
            <Typography>Bridge to start earning {bridgeNumbers} {"Stake\u00B2"} on your {ghstSymbol} balance:</Typography>

            <Box display="flex" justifyContent="center">
                <Typography variant="h5">{formatNumber(estimatedAmount, 5)} {ghstSymbol}</Typography>
            </Box>

            <Typography>
                Generate a unique address for per tx with <Link underline="hover" href={GHOST_CONNECT} color={theme.colors.primary[300]}>GHOST Connect</Link> for privacy.
            </Typography>

            <SwapCard
                id={`bridge-token-receiver`}
                inputWidth={"100%"}
                value={convertedReceiver ? shorten(receiver, 15, -10) : receiver}
                onChange={event => setReceiver(convertedReceiver ? "" : event.currentTarget.value)}
                inputProps={{ "data-testid": "fromInput" }}
                placeholder="GHOST address (sf prefixed)"
                endString={convertedReceiver
                    ? <GhostStyledIcon color="success" viewBox="0 0 25 25" component={CheckCircleIcon} />
                    : undefined
                }
                type="text"
                maxWidth="100%"
            />

            <Box display="flex" justifyContent="center" flexDirection="column" alignItems="center">
                <Box width="100%" display="flex" flexDirection="row" justifyContent="space-between">
                    <Typography variant="body2">Gatekeeper</Typography>
                    <Link
                        fontSize="12px"
                        lineHeight="15px"
                        target="_blank"
                        rel="noopener noreferrer"
                        href={`${chainExplorerUrl}/token/${gatekeeperAddress}`}
                    >
                        <Typography variant="body2">
                            {shorten(gatekeeperAddress, 10, -8)}
                        </Typography>
                    </Link>
                </Box>
                <Box width="100%" display="flex" flexDirection="row" justifyContent="space-between">
                    <Typography variant="body2">Bridge Fee</Typography>
                    <Typography variant="body2">{formatNumber(incomingFee, 4)}%</Typography>
                </Box>
                <Box width="100%" display="flex" flexDirection="row" justifyContent="space-between">
                    <Typography variant="body2">Est. Time</Typography>
                    <Typography variant="body2">20 mins</Typography>
                </Box>
            </Box>

            <PrimaryButton
                onClick={goNext}
                disabled={convertedReceiver === undefined}
                fullWidth
            >
                Proceed
            </PrimaryButton>
        </>
    )
}

const WelcomeView = ({
    bridgeNumbers,
    goNext,
    isStakingOpened,
    chainId,
    warmupPeriod,
    ghstSymbol,
    ftsoSymbol,
    defaultFunction,
    closeModal
}) => {
    const [isPending, setIsPending] = useState(false);

    const { epoch } = useEpoch(chainId);
    const { gatekeeperAddress } = useGatekeeperAddress(chainId);
    const circulatingSupply = useCirculatingSupply(chainId, "STNK");
    const gatekeepedApy = useGatekeeperApy(chainId);

    const { isExtensionMissing } = useUnstableProvider();

    const getConnect = () => {
        window.open(GHOST_CONNECT, '_blank', 'noopener,noreferrer');
        closeModal();
    }

    const apyInner = useMemo(() => {
        let apy = Infinity;
        if (circulatingSupply._value > 0n) {
            const value = epoch.distribute.div(circulatingSupply);
            apy = 100 * (Math.pow(1 + parseFloat(value.toString()), 1095) - 1);
            if (apy === 0) apy = Infinity;
        }
        return apy;
    }, [circulatingSupply, epoch]);

    const callDefaultFunction = useCallback(async () => {
        setIsPending(true);
        await defaultFunction()();
        setIsPending(false);
        closeModal();
    }, [defaultFunction]);

    return (
        <>
            <Typography>{warmupPeriod <= 0
                ? `You've succesfully warmed-up your ${isStakingOpened ? " " : "bonded "}${ftsoSymbol} ${isStakingOpened ? "(3, 3)" : "(1, 1)"} Staked at:`
                : `${isStakingOpened ? "Stake" : "Bond"} is in warm-up${isStakingOpened ? "" : ", which extends with each purchase"}. Your ${ftsoSymbol} ${isStakingOpened ? "(3, 3)" : "(1, 1)"} is Staked at:`
            }</Typography>

            <Box display="flex" justifyContent="center">
                <Typography variant="h5">{formatNumber(apyInner, 2)}% APY</Typography>
            </Box>

            <SecondaryButton
                onClick={() => callDefaultFunction()}
                disabled={isPending || warmupPeriod > 0}
                loading={isPending}
                fullWidth
            >
                {warmupPeriod > 0
                    ? `Warm-up ends in ${prettifySecondsInDays(epoch.length * warmupPeriod)}`
                    : `${isPending ? "Claiming..." : "Claim"} ${isStakingOpened ? "(3, 3) Stake" : "(1, 1) Bond"}`
                }
            </SecondaryButton>

            <Box display="flex" justifyContent="center" flexDirection="column" alignItems="center">
                <hr style={{ width: "100%" }} />
                <Typography variant="h5">OR</Typography>
                <hr style={{ width: "100%" }} />
            </Box>

            <Box display="flex" flexDirection="column" justifyContent="space-between" gap="10px">
                <Typography fontWeight="bold">Skip the Warm-up Now!</Typography>
                <Typography>{`Bridge your ${ghstSymbol} to GHOST Chain and start ${bridgeNumbers} ${"Stake\u00B2"} at:`}</Typography>
            </Box>

            <Box display="flex" justifyContent="center" flexDirection="column" alignItems="center">
                <Typography variant="h5">{formatNumber(apyInner * gatekeepedApy, 2)}% APY</Typography>
            </Box>

            <PrimaryButton
                disabled={isPending || gatekeeperAddress === EMPTY_ADDRESS}
                onClick={isExtensionMissing ? getConnect : goNext}
                fullWidth
            >
                {isExtensionMissing ? "Get GHOST Connect" : `Start ${bridgeNumbers} ${"Stake\u00B2"}`}
            </PrimaryButton>
        </>
    )
}

const ConfirmStep = ({
    chainId,
    address,
    receiver,
    convertedReceiver,
    executableFunction,
    ghstSymbol,
    bridgeNumbers,
    estimatedAmount,
    bridgingRisk,
    incomingFee,
    setActiveTxIndex,
    closeModal,
    evmNetwork
}) => {
    const config = useConfig();
    const navigate = useNavigate();

    const currentSession = useCurrentIndex();
    const { getStorageValue, setStorageValue } = useLocalStorage();

    const [blockNumber, setBlockNumber] = useState(0n);
    const [isPending, setIsPending] = useState(false);
    const [acknowledgeBridgingRisk, setAcknowledgeBridgingRisk] = useState(false);
    const [acknowledgeWalletCustody, setAcknowledgeWalletCustody] = useState(false);

    getBlockNumber(config).then(block => setBlockNumber(block));

    const nativeSymbol = useMemo(() => config?.getClient()?.chain?.nativeCurrency?.symbol, [config]);
    const networkName= useMemo(() => config?.getClient()?.chain?.name.toLowerCase(), [config]);

    const receivedEstimation = useMemo(() => {
        const decimals = incomingFee._decimals + 2;
        const afterFee = new DecimalBigNumber(
            BigInt(Math.pow(10, decimals) - incomingFee._value),
            decimals
        );
        return estimatedAmount.mul(afterFee);
    }, [incomingFee, estimatedAmount]);

    const execute = useCallback(async () => {
        setIsPending(true);
        try {
            const txHash = await executableFunction()(convertedReceiver);
            if (txHash) {
                const expectedSessionIndex = (currentSession ?? 0) + (evmNetwork
                    ? Number((evmNetwork.avg_block_speed * evmNetwork.finality_delay) / (1000n * 14400n))
                    : 0);

                const transaction = {
                    receiverAddress: receiver,
                    amount: estimatedAmount._value.toString(),
                    sessionIndex: expectedSessionIndex,
                    transactionHash: txHash,
                    blockNumber: blockNumber,
                    chainId: chainId,
                    bridgeStability: 69, // TODO: avoid stability
                    timestamp: Date.now()
                }

                const storedTransactions = getStorageValue(chainId, address, "bridge-txs", []);
                const newStoredTransactions = [transaction, ...storedTransactions];
                setStorageValue(chainId, address, "bridge-txs", newStoredTransactions);

                setActiveTxIndex(0);
                navigate(`${networkName}/bridge`);
            }
        } finally {
            setIsPending(false);
            closeModal();
        }
    }, [
        executableFunction,
        convertedReceiver,
        receiver,
        networkName,
        chainId,
        address,
        blockNumber,
        evmNetwork,
        currentSession,
    ]);

    return (
        <>
            <Box display="flex" justifyContent="space-between" alignItems="center">
                <Box display="flex" flexDirection="column" justifyContent="space-between" alignItems="center" gap="10px">
                    <Metric label="To Bridge" metric={formatNumber(estimatedAmount, 5)} />
                    <Box width="100%" display="flex" flexDirection="column" justifyContent="center" alignItems="center">
                        <Token chainTokenName={nativeSymbol} name={"GHST"} sx={{ fontSize: "55px" }} />
                        <Typography>{ghstSymbol}</Typography>
                    </Box>
                </Box>
                <GhostStyledIcon sx={{ transform: "rotate(-90deg)" }} component={ArrowDropDownIcon} />
                <Box display="flex" flexDirection="column" justifyContent="space-between" alignItems="center" gap="10px">
                    <Metric label="To Receive" metric={formatNumber(receivedEstimation, 5)} />
                    <Box width="100%" display="flex" flexDirection="column" justifyContent="center" alignItems="center">
                        <Token name={"GHST"} sx={{ fontSize: "55px" }} />
                        <Typography>{ghstSymbol}</Typography>
                    </Box>
                </Box>
            </Box>

            <Typography>{`You are bridging to GHOST Chain now to claim ${bridgeNumbers} ${"Stake\u00B2"} rewards.`}</Typography>

            <hr style={{ width: "100%" }} />

            <Box display="flex" flexDirection="column" justifyContent="space-between" alignItems="left">
                <FormControlLabel
                    control={
                        <Checkbox
                            data-testid="acknowledge-breakout-warm-up"
                            checked={acknowledgeBridgingRisk}
                            onChange={event => setAcknowledgeBridgingRisk(event.target.checked)}
                            icon={<CheckBoxOutlineBlank viewBox="0 0 24 24" />}
                            checkedIcon={<CheckBoxOutlined viewBox="0 0 24 24" />}
                        />
                    }
                    label={
                        <Typography variant="body2">
                            {`I acknowledge decentralized bridging risk.`}&nbsp;
                            <Link
                                sx={{
                                    margin: "0px",
                                    font: "inherit",
                                    letterSpacing: "inherit",
                                    textDecoration: "underline",
                                    textUnderlineOffset: "0.23rem",
                                    cursor: "pointer",
                                    textDecorationThickness: "1px",
                                    "&:hover": {
                                        textDecoration: "underline",
                                    }
                                }}
                                target="_blank"
                                rel="noopener noreferrer"
                                href="https://ghostchain.io/bridge-disclaimer"
                            >
                                Learn more.
                            </Link>
                        </Typography>
                    }
                    sx={{ '& .MuiFormControlLabel-label': { textAlign: "justify" } }}
                />
                <FormControlLabel
                    control={
                        <Checkbox
                            data-testid="acknowledge-breakout-warm-up"
                            checked={acknowledgeWalletCustody}
                            onChange={event => setAcknowledgeWalletCustody(event.target.checked)}
                            icon={<CheckBoxOutlineBlank viewBox="0 0 24 24" />}
                            checkedIcon={<CheckBoxOutlined viewBox="0 0 24 24" />}
                        />
                    }
                    label={
                        <Typography variant="body2">
                            I confirm that recipient address is a self-custodial wallet, not an exchange, third party service, or smart-contract.
                        </Typography>
                    }
                    sx={{ '& .MuiFormControlLabel-label': { textAlign: "justify" } }}
                />
            </Box>

            <PrimaryButton
                onClick={() => execute()}
                loading={isPending}
                disabled={isPending || !acknowledgeWalletCustody || !acknowledgeBridgingRisk}
                fullWidth
            >
                {isPending ? "Confirming..." : "I Confirm"}
            </PrimaryButton>
        </>
    )
}

export default BreakoutModal;
