import { useEffect, useState, useMemo } from "react";
import ReactGA from "react-ga4";
import { useParams } from 'react-router-dom';
import { useBlock, useBlockNumber } from 'wagmi'

import {
    Box,
    Container,
    Typography,
    Link,
    TableContainer,
    Table,
    TableHead,
    TableRow,
    TableCell,
    TableBody,
    useMediaQuery,
    useTheme
} from "@mui/material";

import Paper from "../../components/Paper/Paper";
import PageTitle from "../../components/PageTitle/PageTitle";
import LinearProgressBar from "../../components/Progress/LinearProgressBar";
import InfoTooltip from "../../components/Tooltip/InfoTooltip";
import Chip from "../../components/Chip/Chip";
import { PrimaryButton, SecondaryButton } from "../../components/Button";

import GhostStyledIcon from "../../components/Icon/GhostIcon";
import ArrowUpIcon from "../../assets/icons/arrow-up.svg?react";

import { formatNumber, formatCurrency, shorten } from "../../helpers";
import { prettifySecondsInDays } from "../../helpers/timeUtil";
import { DecimalBigNumber, } from "../../helpers/DecimalBigNumber";

import { convertStatusToTemplate, convertStatusToLabel } from "./helpers";
import { parseFunctionCalldata } from "./components/functions";

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

import { useTokenSymbol, usePastTotalSupply, usePastVotes, useBalance } from "../../hooks/tokens";
import {
    useProposalState,
    useProposalProposer,
    useProposalLocked,
    useProposalQuorum,
    useProposalVoteOf,
    useProposalVotes,
    useProposalDetails,
    useProposalSnapshot,
    useProposalDeadline,
    useProposalVotingDelay,
    castVote,
    executeProposal,
    releaseLocked
} from "../../hooks/governance";

import { VOTED_PROPOSALS_PREFIX } from "./helpers";

///////////////////////////////////////////////////////
import Timeline from '@mui/lab/Timeline';
import TimelineItem from '@mui/lab/TimelineItem';
import TimelineSeparator from '@mui/lab/TimelineSeparator';
import TimelineConnector from '@mui/lab/TimelineConnector';
import TimelineContent from '@mui/lab/TimelineContent';
import TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';
import TimelineDot from '@mui/lab/TimelineDot';
///////////////////////////
import FastfoodIcon from '@mui/icons-material/Fastfood';
import LaptopMacIcon from '@mui/icons-material/LaptopMac';
import HotelIcon from '@mui/icons-material/Hotel';
import RepeatIcon from '@mui/icons-material/Repeat';

const HUNDRED = new DecimalBigNumber(100n, 0);

const ProposalDetails = ({ chainId, address, connect, config }) => {
    const { id } = useParams();
    const proposalId = BigInt(id);

    const [isPending, setIsPending] = useState(false);
    const [selectedDiscussionUrl, setSelectedDiscussionUrl] = useState(undefined);

    const isSemiSmallScreen = useMediaQuery("(max-width: 745px)");
    const isSmallScreen = useMediaQuery("(max-width: 650px)");
    const isVerySmallScreen = useMediaQuery("(max-width: 379px)");

    const theme = useTheme();

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

    const { state: proposalState } = useProposalState(chainId, proposalId);
    const { proposer: proposalProposer } = useProposalProposer(chainId, proposalId);
    const { locked: proposalLocked } = useProposalLocked(chainId, proposalId);
    const { quorum: proposalQuorum } = useProposalQuorum(chainId, proposalId);

    const { voteOf } = useProposalVoteOf(chainId, proposalId, address);
    const { forVotes, againstVotes, totalVotes } = useProposalVotes(chainId, proposalId);
    const { proposalDetails } = useProposalDetails(chainId, proposalId);
    const { snapshot: proposalSnapshot } = useProposalSnapshot(chainId, proposalId);

    const { pastTotalSupply: totalSupply } = usePastTotalSupply(chainId, "GHST", proposalSnapshot);
    const { pastVotes } = usePastVotes(chainId, "GHST", proposalSnapshot, address);

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

    const quorumPercentage = useMemo(() => {
        if (totalSupply._value === 0n) return 0;
        return proposalQuorum / totalSupply * HUNDRED;
    }, [proposalQuorum, totalSupply]);

    const votePercentage = useMemo(() => {
        if (totalSupply._value === 0n) return 0;
        const value = (totalVotes?._value ?? 0n) * 100n / (totalSupply?._value ?? 1n);
        return new DecimalBigNumber(value, 0);
    }, [totalVotes, totalSupply]);

    const forPercentage = useMemo(() => {
        if (totalSupply._value === 0n) return new DecimalBigNumber(0n, 2);
        const value = (forVotes?._value ?? 0n) * 10000n / (totalSupply?._value ?? 1n);
        return new DecimalBigNumber(value, 2);
    }, [forVotes, totalSupply]);

    const againstPercentage= useMemo(() => {
        if (totalSupply._value === 0n) return new DecimalBigNumber(0n, 2);
        const value = (againstVotes?._value ?? 0n) * 10000n / (totalSupply?._value ?? 1n);
        return new DecimalBigNumber(value, 2);
    }, [againstVotes, totalSupply]);

    const voteWeightPercentage = useMemo(() => {
        if (totalSupply._value === 0n) return new DecimalBigNumber(0n, 2);
        const value = (pastVotes?._value ?? 0n) * 10000n / (totalSupply?._value ?? 1n);
        return new DecimalBigNumber(value, 2);
    }, [pastVotes, totalSupply]);

    const voteValue = useMemo(() => {
        if (totalVotes?._value == 0n) {
            return 0;
        }
        return Number(forVotes._value * 100n / totalVotes._value);
    }, [forVotes, totalVotes]);

    const voteTarget = useMemo(() => {
        const first = (5n * againstVotes._value + forVotes._value);
        const second = BigInt(Math.floor(Math.sqrt(Number(totalVotes._value))));
        const bias = 3n * first + second;
        const denominator = totalVotes._value + bias;

        if (denominator === 0n) {
            return 80;
        }

        return Number(totalVotes?._value * 100n / denominator);
    }, [againstVotes, forVotes, totalVotes]);

    const nativeCurrency = useMemo(() => {
        const client = config?.getClient();
        return client?.chain?.nativeCurrency?.symbol;
    }, [config]);

    const etherscanLink = useMemo(() => {
        const client = config.getClient();
        let url = client?.chain?.blockExplorers?.default?.url;
        if (url) {
            url = url + `/address/${proposalProposer}`;
        }
        return url;
    }, [proposalProposer, config]);

    const handleVote = async (against) => {
        setIsPending(true);
        const support = against ? 0 : 1;
        const result = await castVote(chainId, address, proposalId, support);

        if (result) {
            const toStore = JSON.stringify(proposalId.toString());
            localStorage.setItem(VOTED_PROPOSALS_PREFIX, toStore);
        }

        setIsPending(true);
    }

    const handleExecute = async () => {
        setIsPending(true);
        await executeProposal(chainId, address, proposalId);
        setIsPending(true);
    }

    const handleRelease = async (proposalId) => {
        setIsPending(true);
        await releaseLocked(chainId, address, proposalId);
        setIsPending(true);
    }

    return (
        <Box>
            <PageTitle
                name={`GBP-${id.slice(-5)}`}
                subtitle={
                    <Typography component="span">
                        Proposal details, need more in-depth description
                    </Typography>
                }
            />
            <Container
                style={{
                    paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
                    paddingRight: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
                    minHeight: "calc(100vh - 128px)",
                    display: "flex",
                    flexDirection: "column",
                    justifyContent: "center"
                }}
            >
                <Box sx={{ mt: "15px" }}>
                    <Box display="flex" justifyContent="space-between" gap="20px">
                        <Paper
                            fullWidth
                            enableBackground
                            headerContent={
                                <Box display="flex" alignItems="center" flexDirection="row" gap="10px">
                                    <Typography variant="h6">
                                        Progress
                                    </Typography>
                                    <Chip
                                        sx={{ marginTop: "4px", width: "88px" }}
                                        label={convertStatusToLabel(proposalState)}
                                        template={convertStatusToTemplate(proposalState)}
                                    />
                                </Box>
                            }
                            topRight={
                                <PrimaryButton sx={{ padding: "0 !important"}} variant="text" href={"https://forum.ghostchain.io"} >
                                    View Forum
                                </PrimaryButton>
                            }
                        >
                            <Box height="280px" display="flex" flexDirection="column" justifyContent="space-between" gap="20px">
                                <Box display="flex" flexDirection="column">
                                    <Box display="flex" justifyContent="space-between">
                                        <Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body1" color={theme.colors.feedback.success}>
                                            For: {formatNumber(forVotes.toString(), 2)} ({formatNumber(forPercentage?.toString(), 1)}%)
                                        </Typography>
                                        <Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body1" color={theme.colors.feedback.error}>
                                            Against: {formatNumber(againstVotes.toString(), 2)} ({formatNumber(againstPercentage?.toString(), 1)}%)
                                        </Typography>
                                    </Box>
                                    <LinearProgressBar
                                        barColor={theme.colors.feedback.success}
                                        barBackground={theme.colors.feedback.error}
                                        variant="determinate"
                                        value={voteValue}
                                        target={voteTarget}
                                    />
                                </Box>

                                <Box display="flex" flexDirection="column">
                                    <Box display="flex" justifyContent="space-between">
                                        <Typography>Proposer</Typography>
                                        <Link href={etherscanLink} target="_blank" rel="noopener noreferrer">
                                            {`${shorten(proposalProposer)}`}
                                        </Link>
                                    </Box>
                                    <Box display="flex" justifyContent="space-between">
                                        <Typography>Locked</Typography>
                                        <Typography>{formatCurrency(proposalLocked, 4, ghstSymbol)}</Typography>
                                    </Box>

                                    <hr width="100%" />

                                    <Box display="flex" justifyContent="space-between">
                                        <Box display="flex" flexDirection="row">
                                            <Typography>Quorum</Typography>
                                            <InfoTooltip message={`Minimum $${ghstSymbol} turnout required for the proposal to become valid, as percentage of the total $${ghstSymbol} supply at the time when proposal was created`} />
                                        </Box>
                                        <Typography>{formatNumber(proposalQuorum.toString(), 4)} ({formatNumber(quorumPercentage, 1)}%)</Typography>
                                    </Box>

                                    <Box display="flex" justifyContent="space-between">
                                        <Box display="flex" flexDirection="row">
                                            <Typography>Total</Typography>
                                            <InfoTooltip message={`Total votes for the proposal, as percentage of the total $${ghstSymbol} supply at the time when proposal was created`}/>
                                        </Box>
                                        <Typography>{formatNumber(totalSupply.toString(), 4)} ({formatNumber(votePercentage, 1)}%)</Typography>
                                    </Box>

                                    <Box display="flex" justifyContent="space-between">
                                        <Box display="flex" flexDirection="row">
                                            <Typography>Votes</Typography>
                                            <InfoTooltip message={`Your voting power, as percentage of total $${ghstSymbol} at the time when proposal was created`} />
                                        </Box>
                                        <Typography>{formatNumber(pastVotes.toString(), 4)} ({formatNumber(voteWeightPercentage, 1)}%)</Typography>
                                    </Box>
                                </Box>

                                <Box display="flex" gap="20px">
                                    {address === undefined || address === ""
                                        ? <PrimaryButton fullWidth onClick={() => connect()}>Connect</PrimaryButton>
                                        : voteOf === 0n
                                            ? <>
                                                <SecondaryButton
                                                    fullWidth
                                                    disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending}
                                                    onClick={() => handleVote(1)}
                                                >
                                                    For
                                                </SecondaryButton>
                                                <SecondaryButton
                                                    fullWidth
                                                    disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending}
                                                    onClick={() => handleVote(0)}
                                                >
                                                    Against
                                                </SecondaryButton>
                                            </>
                                        : <SecondaryButton
                                            fullWidth
                                            disabled
                                        >
                                            {`Voted ${voteOf === 1n ? "Against" : "For"}`}
                                        </SecondaryButton>
                                    }
                                </Box>
                            </Box>
                        </Paper>

                        <Paper
                            fullWidth
                            enableBackground
                            headerContent={
                                <Box height="48px" display="flex" alignItems="center" flexDirection="row" gap="5px">
                                    <Typography variant="h6">
                                        Timeline
                                    </Typography>
                                </Box>
                            }
                        >
                            <VotingTimeline
                                proposalLocked={proposalLocked}
                                connect={connect}
                                handleExecute={handleExecute}
                                handleRelease={handleRelease}
                                state={proposalState}
                                address={address}
                                isProposer={proposalProposer === address}
                                chainId={chainId}
                                proposalId={id}
                            />
                        </Paper>
                    </Box>
                </Box>

                <Paper
                    fullWidth
                    enableBackground
                    headerContent={
                        <Box display="flex" alignItems="center" flexDirection="row" gap="5px">
                            <Typography variant="h6">
                                Executable Code
                            </Typography>
                        </Box>
                    }
                >
                    <TableContainer>
                        <Table aria-label="Available bonds" style={{ tableLayout: "fixed" }}>
                            <TableHead>
                                <TableRow>
                                    <TableCell align="center" style={{ padding: "8px 0" }}>Function</TableCell>
                                    <TableCell align="center" style={{ padding: "8px 0" }}>Target</TableCell>
                                    <TableCell align="center" style={{ padding: "8px 0" }}>Calldata</TableCell>
                                    <TableCell align="center" style={{ padding: "8px 0" }}>Value</TableCell>
                                </TableRow>
                            </TableHead>
                            <TableBody>{proposalDetails?.map((metadata, index) => {
                                return parseFunctionCalldata(metadata, index, chainId, nativeCurrency);
                            })}</TableBody>
                        </Table>
                    </TableContainer>
                </Paper>
            </Container>
        </Box>
    )
}

const VotingTimeline = ({ connect, handleExecute, handleRelease, proposalLocked, proposalId, chainId, state, address, isProposer }) => {
    const { delay: propsalVotingDelay  } = useProposalVotingDelay(chainId, proposalId);
    const { snapshot: proposalSnapshot } = useProposalSnapshot(chainId, proposalId);
    const { deadline: proposalDeadline } = useProposalDeadline(chainId, proposalId);

    const voteStarted = useMemo(() => {
        if (proposalSnapshot && propsalVotingDelay) {
            return proposalSnapshot > propsalVotingDelay ? proposalSnapshot - propsalVotingDelay : 0;
        }
        return 0n;
    }, [proposalSnapshot, propsalVotingDelay]);

    return (
        <Box height="280px" display="flex" flexDirection="column" justifyContent="space-between" gap="20px">
            <Timeline sx={{ margin: 0, padding: 0 }}>
                <VotingTimelineItem chainId={chainId} occured={voteStarted} message="Proposed on" isFirst />
                <VotingTimelineItem chainId={chainId} occured={proposalSnapshot} message="Voting started" />
                <VotingTimelineItem chainId={chainId} occured={proposalDeadline} message="Voting ends" />
            </Timeline>
            <Box width="100%" display="flex" gap="10px">
                {isProposer && <SecondaryButton
                    fullWidth
                    disabled={(proposalLocked?._value ?? 0n) === 0n || state < 2}
                    onClick={() => address === "" ? connect() : handleRelease()}
                >
                    {address === "" ? "Connect" : "Release"}
                </SecondaryButton>}
                <SecondaryButton
                    fullWidth
                    disabled={state !== 4}
                    onClick={() => address === "" ? connect() : handleExecute()}
                >
                    {address === "" ? "Connect" : "Execute"}
                </SecondaryButton>
            </Box>
        </Box>
    )
}

const VotingTimelineItem = ({ position, isFirst, isLast, chainId, occured, message }) => {
    const { data: blockNumber } = useBlockNumber({ chainId, watch: true });
    const { data: blockInfo } = useBlock({
        chainId: chainId,
        blockNumber: occured,
        query: {
            enabled: Boolean(occured)
        }
    });

    const timestamp = useMemo(() => {
        if (blockInfo && blockNumber > occured) {
            const timestamp = Number(blockInfo?.timestamp ?? 0n) * 1000;
            return new Date(timestamp).toLocaleString();
        }

        const blocksRemaining = Number(occured) - Number(blockNumber);
        const secondsRemaining = blocksRemaining * Number(networkAvgBlockSpeed(chainId));
        const predictedTimestamp = Math.floor(Date.now() / 1000) + secondsRemaining;

        return new Date(predictedTimestamp * 1000).toLocaleString();
    }, [chainId, occured, blockInfo, blockNumber]);

    return (
        <TimelineItem>
            <TimelineOppositeContent
                sx={{
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "flex-end",
                    padding: "0 16px",
                    minWidth: "140px",
                    flex: 0,
                }}
            >
                <Typography>{message}</Typography>
            </TimelineOppositeContent>

            <TimelineSeparator>
                <TimelineConnector sx={{ background: isFirst ? "transparent" : "#fff" }} />
                <TimelineDot sx={{ width: "15px", height: "15px", background: "#fff", margin: "12px 0" }} />
                <TimelineConnector sx={{ background: isLast ? "transparent" : "#fff" }} />
            </TimelineSeparator>

            <TimelineContent
                sx={{
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "flex-start",
                    padding: "0 26px",
                }}
            >
                <Typography>{timestamp}</Typography>
            </TimelineContent>
        </TimelineItem>
    )
}

export default ProposalDetails;
