import { useEffect, useState, useMemo, useCallback } from "react";
import ReactGA from "react-ga4";

import {
    Box,
    Grid,
    Container,
    Typography,
    Skeleton,
    useMediaQuery,
} from "@mui/material";
import { decodeAddress } from "@polkadot/util-crypto";
import { getBlockNumber } from "@wagmi/core";
import { useTransaction } from "wagmi";
import { keccak256 } from "viem";
import { u32, u64, u128 } from "scale-ts";

import PendingActionsIcon from '@mui/icons-material/PendingActions';
import ArrowBack from '@mui/icons-material/ArrowBack';

import PageTitle from "../../components/PageTitle/PageTitle";
import Paper from "../../components/Paper/Paper";
import GhostStyledIcon from "../../components/Icon/GhostIcon";

import { networkAvgBlockSpeed } from "../../constants";
import { timeConverter } from "../../helpers";

import { useTokenSymbol, useBalance } from "../../hooks/tokens";
import { useGatekeeperAddress } from "../../hooks/staking";

import {
    useEvmNetwork,
    useApplauseDetails,
    useAuthorities,
    useValidators,
    useBlockCommitments,
    useDisabledValidators,
    useCurrentIndex,
    useUnstableProvider,
    useApplauseThreshold,
    useMetadata,
    useCurrentSlot,
    useGenesisSlot,
    useErasTotalStake,
} from "../../hooks/ghost";

import { ValidatorTable } from "./ValidatorTable";
import { BridgeModal, BridgeConfirmModal } from "./BridgeModal";
import { BridgeHeader } from "./BridgeHeader";
import { BridgeCardAction, BridgeCardHistory } from "./BridgeCard";

const STORAGE_PREFIX = "storedTransactions"

const Bridge = ({ chainId, address, config, connect }) => {
    const isBigScreen = useMediaQuery("(max-width: 980px)")
    const isSmallScreen = useMediaQuery("(max-width: 650px)");
    const isSemiSmallScreen = useMediaQuery("(max-width: 540px)");
    const isVerySmallScreen = useMediaQuery("(max-width: 379px)");

    const [bridgeModalOpen, setBridgeModalOpen] = useState(false);
    const [isConfirmed, setIsConfirmed] = useState(false);
    const [activeTxIndex, setActiveTxIndex] = useState(-1);
    const [blockNumber, setBlockNumber] = useState(0n);
    const [bridgeAction, setBridgeAction] = useState(true);
    const [currentTime, setCurrentTime] = useState(Date.now());

    useEffect(() => {
        const interval = setInterval(() => setCurrentTime(Date.now()), 1000);
        return () => clearInterval(interval);
    }, []);

    const initialStoredTransactions = localStorage.getItem(STORAGE_PREFIX);
    const [storedTransactions, setStoredTransactions] = useState(
        initialStoredTransactions ? JSON.parse(initialStoredTransactions) : []
    );

    const { gatekeeperAddress } = useGatekeeperAddress(chainId);
    const gatekeeperAddressEmpty = useMemo(() => {
         if (gatekeeperAddress === "0x0000000000000000000000000000000000000000") {
             return true;
         }
         return false;
     }, [gatekeeperAddress]);

    const { providerDetail } = useUnstableProvider();
    const metadata = useMetadata();

    const watchTransaction = useMemo(() => {
        if (activeTxIndex < 0 || activeTxIndex >= storedTransactions?.length) {
            return undefined
        }
        return storedTransactions?.at(activeTxIndex)
    }, [activeTxIndex, storedTransactions]);

    const { data: watchTransactionInfo } = useTransaction({
        hash: watchTransaction?.transactionHash
    });

    const hashedArguments = useMemo(() => {
        if (!watchTransaction) return undefined

        const networkIdEncoded = u64.enc(BigInt(chainId));
        const amountEncoded = u128.enc(BigInt(watchTransaction.amount));
        const addressEncoded = decodeAddress(watchTransaction.receiverAddress, false, 1996);
        const blockNumber = u64.enc(watchTransactionInfo?.blockNumber ?? 0n);

        const clapArgsStr = new Uint8Array([
            ...addressEncoded,
            ...amountEncoded,
            ...blockNumber,
            ...networkIdEncoded
        ]);
        return keccak256(clapArgsStr)
    }, [watchTransaction, watchTransactionInfo])

    const currentSlot = useCurrentSlot();
    const genesisSlot = useGenesisSlot();
    const currentSession = useCurrentIndex();
    const applauseThreshold = useApplauseThreshold();
    const evmNetwork = useEvmNetwork({ evmChainId: chainId });
    const totalStakedAmount = useErasTotalStake({
        eraIndex: Math.floor((watchTransaction?.sessionIndex ?? currentSession) / 6)
    });
    const authorities = useAuthorities({
        currentSession: watchTransaction?.sessionIndex ?? currentSession
    });
    const validators = useValidators({
        currentSession: watchTransaction?.sessionIndex ?? currentSession
    });
    const blockCommitments = useBlockCommitments({ evmChainId: chainId });
    const disabledValidators = useDisabledValidators();
    const transactionApplaused = useApplauseDetails({
        currentSession: watchTransaction?.sessionIndex ?? currentSession,
        argsHash: hashedArguments
    });

    const finalityDelay = Number(evmNetwork?.finality_delay ?? 0n);

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

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

    useEffect(() => {
        ReactGA.send({ hitType: "pageview", page: "/bridge" });
    }, []);

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

    const currentRecord = useMemo(() => {
        if (!watchTransaction) return undefined

        const countOnesInBigInt = (n) => {
            let count = 0;
            let tempN = n;
            while (tempN > 0n) {
                tempN &= (tempN - 1n);
                count++;
            }
            return count;
        }

        const numberOfClaps = transactionApplaused?.authorities.buckets.reduce((sum, bucketPair) => {
            const bigIntValue = bucketPair.at(1);
            return sum + countOnesInBigInt(bigIntValue);
        }, 0);

        const finalization = Math.max(0, (finalityDelay + watchTransaction.blockNumber) - Number(blockNumber));
        const applaused = transactionApplaused?.finalized ?? false;
        const clappedAmount = transactionApplaused?.clapped_amount ?? 0n;
        const clappedPercentage = clappedAmount * 100n / (totalStakedAmount ?? 1n);
        const step = finalization > 0
            ? 0
            : applaused ? 2 : 1;

        return {
            ...watchTransaction,
            finalization,
            applaused,
            numberOfClaps,
            clappedAmount,
            clappedPercentage,
            step,
        }
    }, [
        transactionApplaused,
        finalityDelay,
        watchTransaction,
        blockNumber,
        totalStakedAmount
    ]);

    const filteredStoredTransactions = useMemo(() => {
        return storedTransactions.filter(obj => obj.chainId === chainId);
    }, [storedTransactions, chainId]);

    const latestCommits = useMemo(() => {
        return validators?.map((validator, index) => {
            return {
                validator: validator,
                lastActive: currentTime - Number(blockCommitments?.at(index)?.last_updated ?? 0),
                lastUpdated: blockCommitments?.at(index)?.last_updated,
                lastStoredBlock: blockCommitments?.at(index)?.last_stored_block,
                storedBlockTime: (blockCommitments?.at(index)?.last_stored_block ?? 0n) * networkAvgBlockSpeed(chainId),
                disabled: disabledValidators?.includes(index),
            }
        })
    }, [blockCommitments, disabledValidators, validators, chainId]);

    const latestUpdate = useMemo(() => {
        const validCommits = latestCommits?.filter(commit =>
            !commit.disabled && commit.lastActive != null
        ) || [];

        if (validCommits.length === 0) return null;

        return Math.min(...validCommits.map(commit => commit.lastActive));
    }, [latestCommits])

    const slowestEvmBlock = useMemo(() => {
        if (latestCommits?.length === 0) {
            return 0n;
        }
        const slowestValidator = latestCommits?.reduce((min, commit) => {
            return commit.lastStoredBlock < min.lastStoredBlock && !commit.disabled ? commit : min
        });
        if (!blockNumber || !slowestValidator) {
            return 0n;
        }
        return blockNumber * networkAvgBlockSpeed(chainId) - slowestValidator?.storedBlockTime;
    }, [latestCommits, blockNumber, chainId]);

    const bridgeStability = useMemo(() => {
        const length = latestCommits?.length ?? 0;
        if (length === 0) {
            return 0;
        }

        const blocksInFourHours = 14400n / networkAvgBlockSpeed(chainId);
        let certainty = 0n;
        for (let i = 0; i < length; i++) {
            const commit = latestCommits.at(i);
            if (commit.disabled || blockNumber < blocksInFourHours) {
                continue;
            }
            certainty += (commit?.lastStoredBlock ?? 0n) - (blockNumber - blocksInFourHours);
        }
        return Math.max(Number(certainty * 100n / (blocksInFourHours * BigInt(length))), 0);
    }, [latestCommits, blockNumber]);

    const timeToNextEpoch = useMemo(() => {
        if (!currentSession || !genesisSlot || !currentSlot) {
            return undefined;
        }

        const valueOrDefault = (value) => value ?? 0n;
        const blocks = (BigInt(currentSession ?? 0) + 1n) * 2400n + valueOrDefault(genesisSlot) - valueOrDefault(currentSlot);
        return blocks * 6n;
    }, [currentSession, genesisSlot, currentSlot, currentTime]);

    const removeStoredRecord = useCallback(() => {
        const newStoredTransactions = storedTransactions.filter((_, index) => index !== activeTxIndex)
        setStoredTransactions(newStoredTransactions);
        localStorage.setItem(storagePrefix, JSON.stringify(newStoredTransactions));
        setActiveTxIndex(-1);
    }, [storedTransactions, activeTxIndex, setStoredTransactions, setActiveTxIndex]);

    const handleButtonProceed = () => {
        setBridgeModalOpen(false);
        setIsConfirmed(true);
    }

    const storeTransactionHash = (txHash, receiver, amount) => {
        const transaction = {
            sessionIndex: currentSession ?? 0,
            transactionHash: txHash,
            receiverAddress: receiver,
            amount: amount,
            chainId: chainId,
            blockNumber: Number(blockNumber),
            bridgeStability: bridgeStability,
            timestamp: Date.now()
        }

        const newStoredTransactions = [transaction, ...storedTransactions];
        setStoredTransactions(newStoredTransactions);
        localStorage.setItem(STORAGE_PREFIX, JSON.stringify(newStoredTransactions));
        setActiveTxIndex(newStoredTransactions.length - 1)
    }

    return (
        <>
            <PageTitle name="GHOST Bridge" subtitle="The only pure Web3 decentralized bridge." />
            <Container
                style={{
                    paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
                    paddingRight: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
                    display: "flex",
                    justifyContent: "center",
                    alignItems: "center",
                }}
            >
                <BridgeConfirmModal
                    bridgeStability={bridgeStability}
                    isOpen={bridgeModalOpen}
                    setClose={() => setBridgeModalOpen(false)}
                    handleButtonProceed={handleButtonProceed}
                />
                <BridgeModal
                    currentRecord={currentRecord}
                    activeTxIndex={activeTxIndex}
                    setActiveTxIndex={setActiveTxIndex}
                    authorities={authorities}
                    ghstSymbol={ghstSymbol}
                    hashedArguments={hashedArguments}
                    chainExplorerUrl={chainExplorerUrl}
                    removeStoredRecord={removeStoredRecord}
                />
                <Box minHeight="calc(100vh - 135px)" display="flex" flexDirection="column" justifyContent="center" sx={{ mt: "15px" }}>
                    <Grid container spacing={1}>
                        <Grid item xs={12} height="100%">
                            <Paper
                                sx={{ height: isBigScreen ? "100%" : "150px", paddingBottom: "0 !important", marginBottom: "0 !important" }}
                                fullWidth
                                enableBackground
                            >
                                <BridgeHeader
                                    totalValidators={validators?.length}
                                    disabledValidators={disabledValidators?.length}
                                    bridgeStability={bridgeStability ? Number(bridgeStability) : undefined}
                                    transactionEta={slowestEvmBlock ? Number(slowestEvmBlock) : undefined}
                                    timeToNextEpoch={timeToNextEpoch ? Number(timeToNextEpoch) : undefined}
                                    isSmallScreen={isSmallScreen}
                                />
                            </Paper>
                        </Grid>
                        <Grid container spacing={2} style={{ marginTop: '10px' }}>
                            <Grid item xs={isBigScreen ? 12 : 5} style={{ paddingLeft: "23px" }}>
                                <Paper
                                    headerContent={
                                        <Box alignItems="center" justifyContent="start" display="flex" width="100%">
                                            {!bridgeAction && (<GhostStyledIcon
                                                component={ArrowBack}
                                                viewBox="0 0 23 23"
                                                style={{ marginRight: "20px", display: "flex", alignSelf: "center", cursor: "pointer" }}
                                                onClick={() => setBridgeAction(!bridgeAction)}
                                            />)}
                                            <Typography variant="h4">{
                                                bridgeAction ? `Bridge $${ghstSymbol}` : "Transaction History"
                                            }</Typography>
                                        </Box>
                                    }
                                    topRight={bridgeAction && (
                                        <Box alignItems="center" justifyContent="center" display="flex" height="100%">
                                            <GhostStyledIcon
                                                component={PendingActionsIcon}
                                                viewBox="0 0 23 23"
                                                style={{ display: "flex", alignItems: "center", cursor: "pointer" }}
                                                onClick={() => setBridgeAction(!bridgeAction)}
                                            />
                                        </Box>
                                    )}
                                    style={{ height: "434px" }}
                                    fullWidth
                                    enableBackground
                                >
                                    {bridgeAction
                                        ? <BridgeCardAction
                                            isVerySmallScreen={isVerySmallScreen}
                                            isSemiSmallScreen={isSemiSmallScreen}
                                            chainId={chainId}
                                            address={address}
                                            ghstSymbol={ghstSymbol}
                                            gatekeeperAddressEmpty={gatekeeperAddressEmpty}
                                            gatekeeperAddress={gatekeeperAddress}
                                            evmNetwork={evmNetwork}
                                            connect={connect}
                                            isConfirmed={isConfirmed}
                                            setIsConfirmed={setIsConfirmed}
                                            openBridgeModal={() => setBridgeModalOpen(true)}
                                            storeTransactionHash={storeTransactionHash}
                                        />
                                        : <BridgeCardHistory
                                            isSemiSmallScreen={isSemiSmallScreen}
                                            isBigScreen={isBigScreen}
                                            filteredStoredTransactions={filteredStoredTransactions}
                                            ghstSymbol={ghstSymbol}
                                            blockNumber={blockNumber}
                                            finalityDelay={finalityDelay}
                                            setActiveTxIndex={setActiveTxIndex}
                                        />
                                    }
                                </Paper>
                            </Grid>
                            {!isSemiSmallScreen && (<Grid item xs={isBigScreen ? 12 : 7} style={{ paddingLeft: isBigScreen ? "23px": "" }}>
                                <Paper
                                    headerContent={
                                        <Box alignItems="center" justifyContent="start" display="flex" width="100%">
                                            <Typography variant="h4">Real-Time Bridge Stats</Typography>
                                        </Box>
                                    }
                                    topRight={
                                        <Box alignItems="center" justifyContent="center" display="flex" height="100%" width="180px">
                                            <Typography variant="body1">{latestUpdate
                                                ? `Last update: ${timeConverter(Math.floor(latestUpdate / 1000))}`
                                                : <Skeleton width="180px" />
                                            }</Typography>
                                        </Box>
                                    }
                                    style={{ height: "434px" }}
                                    enableBackground
                                    fullWidth
                                >
                                    <ValidatorTable
                                        currentTime={currentTime}
                                        currentBlock={blockNumber}
                                        latestCommits={latestCommits}
                                        bridgeStability={bridgeStability}
                                        isVerySmallScreen={isVerySmallScreen}
                                        providerDetail={providerDetail}
                                    />
                                </Paper>
                            </Grid>)}
                        </Grid>
                    </Grid>
                </Box>
            </Container>
        </>
    )
}

export default Bridge;
