import { useEffect, useState, useMemo, useCallback } from "react";
import ReactGA from "react-ga4";
import { useParams, useLocation } from 'react-router-dom';
import { useQueryClient } from '@tanstack/react-query';
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 { convertStatusToColor, convertStatusToLabel } from "./helpers";
import { parseFunctionCalldata } from "./components/functions";

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

import { useLocalStorage } from "../../hooks/localstorage";
import { useTokenSymbol, usePastTotalSupply, usePastVotes, useBalance } from "../../hooks/tokens";
import {
    getVoteValue,
    getVoteTarget,
    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 location = useLocation();
    const { id } = useParams();
    const proposalId = BigInt(id);

    const [isPendingVote, setIsPendingVote] = useState(-1);
    const [isPendingExecute, setIsPendingExecute] = useState(false);
    const [isPendingRelease, setIsPendingRelease] = 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 queryClient = useQueryClient();
    const { getStorageValue, setStorageValue } = useLocalStorage();

    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: `${chainId}/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;
        return totalVotes * HUNDRED / totalSupply;
    }, [totalVotes, totalSupply]);

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

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

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

    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 isPending = useMemo(() => {
        return isPendingExecute || isPendingRelease || isPendingVote > -1;
    }, [isPendingExecute, isPendingRelease, isPendingVote]);

    const handleVote = useCallback(async (support) => {
        setIsPendingVote(support);
        const result = await castVote(chainId, address, proposalId, support);

        if (result) {
            const storedVotedProposals = getStorageValue(chainId, address, VOTED_PROPOSALS_PREFIX, []);
            const proposals = JSON.parse(storedVotedProposals || "[]").map(id => BigInt(id));
            proposals.push(proposalId);
            setStorageValue(chainId, address, VOTED_PROPOSALS_PREFIX, proposals.map(id => id.toString()));
            await queryClient.invalidateQueries();
        }
        setIsPendingVote(-1);
    }, [chainId, address, proposalId]);

    const handleExecute = async () => {
        setIsPendingExecute(true);
        await executeProposal(chainId, address, proposalId);
        await queryClient.invalidateQueries();
        setIsPendingExecute(false);
    }

    const handleRelease = async () => {
        setIsPendingRelease(true);
        await releaseLocked(chainId, address, proposalId);
        await queryClient.invalidateQueries();
        setIsPendingRelease(false);
    }

    return (
        <Box>
            <PageTitle
                name={`GDP-${id.slice(-5)}`}
                subtitle={
                    <Typography component="span">
                        {`Cast ${ghstSymbol} to shape this proposal's outcome`}
                    </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
                        gap="20px"
                        display="flex"
                        justifyContent={"space-between"}
                        flexDirection={isSmallScreen ? "column" : "row"}
                    >
                        <Paper
                            fullWidth
                            enableBackground
                            headerContent={
                                <Box display="flex" alignItems="center" flexDirection="row" gap="10px">
                                    {!isSmallScreen &&<Typography variant="h6">
                                        Progress
                                    </Typography>}
                                    <Chip
                                        sx={{ marginTop: "4px", width: "88px" }}
                                        label={convertStatusToLabel(proposalState)}
                                        background={convertStatusToColor(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(), 2)}%)
                                        </Typography>
                                        <Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body1" color={theme.colors.feedback.error}>
                                            Against: {formatNumber(againstVotes.toString(), 2)} ({formatNumber(againstPercentage?.toString(), 2)}%)
                                        </Typography>
                                    </Box>
                                    <LinearProgressBar
                                        barColor={theme.colors.feedback.success}
                                        barBackground={theme.colors.feedback.error}
                                        variant="determinate"
                                        value={getVoteValue(forVotes?._value ?? 0n, totalVotes?._value ?? 0n)}
                                        target={getVoteTarget(totalVotes?._value ?? 0n, totalSupply?._value ?? 0n)}
                                    />
                                </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, 5, 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(), 5)} ({formatNumber(quorumPercentage, 2)}%)</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(), 5)} ({formatNumber(votePercentage, 2)}%)</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(), 5)} ({formatNumber(voteWeightPercentage, 2)}%)</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}
                                                    loading={isPendingVote === 1}
                                                    onClick={() => handleVote(1)}
                                                >
                                                    {isPendingVote === 1 ? "Voting For..." : "For"}
                                                </SecondaryButton>
                                                <SecondaryButton
                                                    fullWidth
                                                    disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending}
                                                    loading={isPendingVote === 0}
                                                    onClick={() => handleVote(0)}
                                                >
                                                    {isPendingVote === 0 ? "Voting Against..." : "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}
                                isPending={isPending}
                                isPendingExecute={isPendingExecute}
                                isPendingRelease={isPendingRelease}
                            />
                        </Paper>
                    </Box>
                </Box>

                <Paper
                    fullWidth
                    enableBackground
                    headerContent={
                        <Box display="flex" alignItems="center" flexDirection="row" gap="5px">
                            <Typography variant="h6">
                                Executable Code
                            </Typography>
                        </Box>
                    }
                >
                    {isSmallScreen
                        ? <Box display="flex" flexDirection="column">
                            {proposalDetails?.map((metadata, index) => {
                                return parseFunctionCalldata({
                                    metadata,
                                    index,
                                    chainId,
                                    nativeCoin: nativeCurrency,
                                    isTable: false,
                                 });
                            })}
                        </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,
                                        nativeCoin: nativeCurrency,
                                     });
                                })}</TableBody>
                            </Table>
                        </TableContainer>
                    }
                </Paper>
            </Container>
        </Box>
    )
}

const VotingTimeline = ({
    connect,
    handleExecute,
    handleRelease,
    proposalLocked,
    proposalId,
    chainId,
    state,
    address,
    isProposer,
    isPending,
    isPendingExecute,
    isPendingRelease,
}) => {
    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 && (proposalLocked?._value ?? 0n) > 0n) && <SecondaryButton
                    fullWidth
                    loading={isPendingRelease}
                    disabled={isPending || state < 2}
                    onClick={() => address === "" ? connect() : handleRelease()}
                >
                    {address === "" ? "Connect" : isPendingRelease ? "Releasing..." : "Release"}
                </SecondaryButton>}
                <SecondaryButton
                    fullWidth
                    loading={isPending}
                    disabled={isPending || state !== 4}
                    onClick={() => address === "" ? connect() : handleExecute()}
                >
                    {address === ""
                        ? "Connect"
                        : state !== 4
                            ? convertStatusToLabel(state)
                            : isPendingExecute ? "Executing..." : "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;
