import {
    Box,
    Container,
    Divider,
    Typography,
    InputLabel,
    FormControl,
    OutlinedInput,
    useMediaQuery,
    useTheme,
} from "@mui/material";
import SettingsIcon from '@mui/icons-material/Settings';
import { useEffect, useMemo, useState } from "react";
import { useParams, useLocation, useSearchParams } from "react-router-dom";
import { Helmet } from "react-helmet";
import ReactGA from "react-ga4";

import InfoTooltip from "../../components/Tooltip/InfoTooltip";
import Modal from "../../components/Modal/Modal";
import PageTitle from "../../components/PageTitle/PageTitle";
import Paper from "../../components/Paper/Paper";
import SwapCard from "../../components/Swap/SwapCard";
import GhostStyledIcon from "../../components/Icon/GhostIcon";
import { Tab, Tabs } from "../../components/Tabs/Tabs";

import {
    UNISWAP_V2_ROUTER,
    UNISWAP_V2_FACTORY,
    RESERVE_ADDRESSES,
    FTSO_ADDRESSES,
    EMPTY_ADDRESS,
    WETH_ADDRESSES,
} from "../../constants/addresses";
import { useTokenSymbol } from "../../hooks/tokens";
import { getTokenAddress } from "../../hooks/helpers";

import PoolContainer from "./PoolContainer";
import SwapContainer from "./SwapContainer";
import TokenModal from "./TokenModal";

const Dex = ({ chainId, address, connect, config }) => {
    const location = useLocation();
    const pathname = useParams();

    const theme = useTheme();

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

    const [currentQueryParameters, setSearchParams] = useSearchParams();
    const newQueryParameters = new URLSearchParams();

    const [isSwap, setIsSwap] = useState(false);
    const [settingsOpen, handleSettingsOpen] = useState(false);
    const [topTokenListOpen, setTopTokenListOpen] = useState(false);
    const [bottomTokenListOpen, setBottomTokenListOpen] = useState(false);

    const [secondsToWait, setSecondsToWait] = useState(localStorage.getItem("dex-deadline") || "60");
    const [slippage, setSlippage] = useState(localStorage.getItem("dex-slippage") || "5");
    const [formatDecimals, setFormatDecimals] = useState(localStorage.getItem("dex-decimals") || "5");
    const [actualDestinationAddress, setActualDestinationAddress] = useState(localStorage.getItem("dex-destination"));
    const [destinationAddress, setDestinationAddress] = useState(actualDestinationAddress);

    const [tokenAddressTop, setTokenAddressTop] = useState(EMPTY_ADDRESS);
    const [tokenAddressBottom, setTokenAddressBottom] = useState(FTSO_ADDRESSES[chainId]);

    const { symbol: tokenNameTopInner } = useTokenSymbol(chainId, tokenAddressTop);
    const { symbol: tokenNameBottomInner } = useTokenSymbol(chainId, tokenAddressBottom);

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

    const tokenNameTop = useMemo(() => {
        if (chainSymbol && tokenAddressTop === EMPTY_ADDRESS) {
            return chainSymbol;
        }
        return tokenNameTopInner;
    }, [tokenAddressTop, tokenNameTopInner, chainSymbol]);

    const tokenNameBottom = useMemo(() => {
        const chainSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
        if (chainSymbol && tokenAddressBottom === EMPTY_ADDRESS) {
            return chainSymbol;
        }
        return tokenNameBottomInner;
    }, [tokenAddressBottom, tokenNameBottomInner, config]);

    const isWrapping = useMemo(() => {
        const isNative = tokenAddressTop === EMPTY_ADDRESS;
        const isWrappedNative = tokenAddressBottom === WETH_ADDRESSES[chainId];
        return isNative && isWrappedNative;
    }, [chainId, tokenAddressTop, tokenAddressBottom]);

    const isUnwrapping = useMemo(() => {
        const isWrappedNative = tokenAddressTop === WETH_ADDRESSES[chainId];
        const isNative = tokenAddressBottom === EMPTY_ADDRESS;
        return isNative && isWrappedNative;
    }, [chainId, tokenAddressTop, tokenAddressBottom]);

    useEffect(() => {
        if (currentQueryParameters.has("pool")) {
            setIsSwap(false);
            newQueryParameters.set("pool", true);
        } else {
            setIsSwap(true);
            newQueryParameters.delete("pool");
        }

        if (currentQueryParameters.has("from")) {
            setTokenAddressTop(currentQueryParameters.get("from"));
            newQueryParameters.set("from", currentQueryParameters.get("from"));
        } else {
            setTokenAddressTop(EMPTY_ADDRESS);
            newQueryParameters.set("from", EMPTY_ADDRESS);
        }

        if (currentQueryParameters.has("to")) {
            setTokenAddressBottom(currentQueryParameters.get("to"));
            newQueryParameters.set("to", currentQueryParameters.get("to"));
        } else {
            setTokenAddressBottom(FTSO_ADDRESSES[chainId]);
            newQueryParameters.set("to", FTSO_ADDRESSES[chainId]);
        }

        setSearchParams(newQueryParameters)
    }, [currentQueryParameters])

    useEffect(() => {
        ReactGA.send({ hitType: "pageview", page: location.pathname + location.search });
    }, [location]);

    const dexAddresses = {
        router: UNISWAP_V2_ROUTER[chainId],
        factory: UNISWAP_V2_FACTORY[chainId],
    }

    const onCardsSwap = () => {
        const tmpFrom = currentQueryParameters.get("from");
        const tmpTo = currentQueryParameters.get("to");

        if (currentQueryParameters.has("pool")) newQueryParameters.set("pool", true);
        else newQueryParameters.delete("pool");

        newQueryParameters.set("from", tmpTo);
        newQueryParameters.set("to", tmpFrom);

        setSearchParams(newQueryParameters);
    }

    const changeSwapTab = (swap) => {
        if (swap || (isWrapping || isUnwrapping)) newQueryParameters.delete("pool");
        else newQueryParameters.set("pool", true);
        newQueryParameters.set("from", currentQueryParameters.get("from"));
        newQueryParameters.set("to", currentQueryParameters.get("to"));
        setSearchParams(newQueryParameters);
    }

    const setInnerTokenAddressTop = (tokenAddress) => {
        if (currentQueryParameters.has("pool")) newQueryParameters.set("pool", true);
        else newQueryParameters.delete("pool");

        if (currentQueryParameters.get("to") === tokenAddress) {
            newQueryParameters.set("from", currentQueryParameters.get("from"));
        } else newQueryParameters.set("from", tokenAddress);
        newQueryParameters.set("to", currentQueryParameters.get("to"));
        setSearchParams(newQueryParameters);
    }

    const setInnerTokenAddressBottom = (tokenAddress) => {
        if (currentQueryParameters.has("pool")) newQueryParameters.set("pool", true);
        else newQueryParameters.delete("pool");

        newQueryParameters.set("from", currentQueryParameters.get("from"));
        if (currentQueryParameters.get("from") === tokenAddress) {
            newQueryParameters.set("to", currentQueryParameters.get("to"));
        } else newQueryParameters.set("to", tokenAddress);
        setSearchParams(newQueryParameters);
    }

    const setSlippageInner = (value) => {
        const maybeValue = parseFloat(value);
        if (!maybeValue || parseFloat(value) <= 100) {
            setSlippage(value);
            localStorage.setItem("dex-slippage", value);
        }
    }

    const setSecondsToWaitInner = (value) => {
        localStorage.setItem("dex-deadline", value);
        setSecondsToWait(value);
    }

    const setFormatDecimalsInner = (value) => {
        if (Number(value) <= 17) {
            localStorage.setItem("dex-decimals", value);
            setFormatDecimals(value);
        }
    }

    const setDestinationAddressInner = (value) => {
        const cleanedValue = value.trim();
        const isEvmAddress = /^0x[a-fA-F0-9]{40}$/.test(cleanedValue);
        if (isEvmAddress) {
            localStorage.setItem("dex-destination", value);
            setActualDestinationAddress(value);
        } else if (!isEvmAddress && actualDestinationAddress) {
            localStorage.removeItem("dex-destination");
            setActualDestinationAddress(undefined);
        }
        setDestinationAddress(value);
    }

    const handleCloseSetting = () => {
        setDestinationAddress(undefined);
        handleSettingsOpen(false);
    }

    return (
        <Box height="calc(100vh - 43px)">
            <Helmet>
                <title>ghostSwap | The pure web3 legacy v2 swap</title>
                <meta name="description" content="ghostSwap carries the legacy of V2 DEX interfaces - no KYC, no bloatware, just pure decentralized swapping. Enjoy gas-efficient trades, deep liquidity, and full self-custody, enabling token swaps as DeFi intended." />
                <meta name="keywords" content="ghostSwap, Uniswap, Uniswap V2, Sushiswap, Sushiswap v2, Pancakeswap, Pancakeswap v2, Bancor, DEX, swap, web3, LP, liquidity provider, decentralized exchange, legacy v2, legacy v2 swap" />

                <meta property="og:image" content="https://ghostchain.io/wp-content/uploads/2025/03/ghostSwap-Featured_Image.png" />
                <meta property="og:title" content="ghostDAO | The DeFi 2.0 cross-chain reserve currency" />
                <meta property="og:image:type" content="image/png" />
                <meta property="og:image:width" content="1200" />
                <meta property="og:image:height" content="630" />
                <meta property="og:type" content="website" />
                <meta property="og:description" content="ghostSwap carries the legacy of V2 DEX interfaces - no KYC, no bloatware, just pure decentralized swapping. Enjoy gas-efficient trades, deep liquidity, and full self-custody, enabling token swaps as DeFi intended." />

                <meta name="twitter:card" content="summary" />
                <meta name="twitter:site" content="@realGhostChain" />
                <meta name="twitter:title" content="ghostDAO | The DeFi 2.0 cross-chain reserve currency" />
                <meta name="twitter:description" content="ghostSwap carries the legacy of V2 DEX interfaces - no KYC, no bloatware, just pure decentralized swapping. Enjoy gas-efficient trades, deep liquidity, and full self-custody, enabling token swaps as DeFi intended." />
                <meta name="twitter:image" content="https://ghostchain.io/wp-content/uploads/2025/03/ghostSwap-Featured_Image.png" />
            </Helmet>

            <PageTitle
                name={`${pathname.name.charAt(0).toUpperCase() + pathname.name.slice(1).toLowerCase()} V2 Classic`}
                subtitle="Swap via Uniswap V2 Fork"
            />
            <Container
                style={{
                    paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
                    paddingRight: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
                    height: "calc(100vh - 153px)",
                    display: "flex",
                    justifyContent: "center",
                    alignItems: "center"
                }}
            >
                <Modal
                    maxWidth="376px"
                    minHeight="200px"
                    open={settingsOpen}
                    headerText={"Settings"}
                    onClose={() => handleCloseSetting()}
                >
                    <Box>
                        <InputLabel htmlFor="slippage">Slippage</InputLabel>
                        <Box mt="8px">
                            <FormControl variant="outlined" color="primary" fullWidth>
                                <OutlinedInput
                                    inputProps={{ "data-testid": "slippage-dex" }}
                                    type="text"
                                    id="slippage-dex"
                                    value={slippage}
                                    onChange={event => setSlippageInner(event.currentTarget.value)}
                                    endAdornment="%"
                                />
                            </FormControl>
                        </Box>
                        <Box mt="8px">
                            <Typography variant="body2" color="textSecondary">
                                Transaction may revert if price changes by more than slippage %
                            </Typography>
                        </Box>
                    </Box>

                    <Box mt="32px">
                        <InputLabel htmlFor="recipient">Transaction deadline</InputLabel>
                        <Box mt="8px">
                            <FormControl variant="outlined" color="primary" fullWidth>
                                <OutlinedInput
                                    inputProps={{ "data-testid": "seconds-to-wait" }}
                                    type="number"
                                    id="seconds-to-wait"
                                    value={secondsToWait}
                                    onChange={event => setSecondsToWaitInner(event.currentTarget.value)}
                                    endAdornment="seconds"
                                />
                            </FormControl>
                        </Box>
                        <Box mt="8px">
                            <Typography variant="body2" color="textSecondary">
                                How long transaction is valid in the transaction pool
                            </Typography>
                        </Box>
                    </Box>

                    <Box mt="32px">
                        <InputLabel htmlFor="recipient">Decimal representation</InputLabel>
                        <Box mt="8px">
                            <FormControl variant="outlined" color="primary" fullWidth>
                                <OutlinedInput
                                    inputProps={{ "data-testid": "decimals-to-wait" }}
                                    type="number"
                                    id="decimals-to-wait"
                                    value={formatDecimals}
                                    onChange={event => setFormatDecimalsInner(event.currentTarget.value)}
                                    endAdornment="decimals"
                                />
                            </FormControl>
                        </Box>
                        <Box mt="8px">
                            <Typography variant="body2" color="textSecondary">
                                Number of decimals to be shown in token balances
                            </Typography>
                        </Box>
                    </Box>

                    <Box mt="32px">
                        <InputLabel htmlFor="recipient">
                            {`${actualDestinationAddress ? "Custom" : "Default"} destination address`}
                        </InputLabel>
                        <Box mt="8px">
                            <FormControl variant="outlined" color="primary" fullWidth>
                                <OutlinedInput
                                    inputProps={{ "data-testid": "decimals-to-wait" }}
                                    type="text"
                                    id="destination-to-wait"
                                    value={destinationAddress
                                        ? destinationAddress
                                        : actualDestinationAddress ?? address
                                    }
                                    onChange={event => setDestinationAddressInner(event.currentTarget.value)}
                                />
                            </FormControl>
                        </Box>
                        <Box mt="8px">
                            <Typography variant="body2" color="textSecondary">
                                Recipient address of swapped assets and liquidity tokens
                            </Typography>
                        </Box>
                    </Box>
                </Modal>

                <TokenModal
                    chainSymbol={chainSymbol}
                    account={address}
                    chainId={chainId}
                    listOpen={topTokenListOpen}
                    setListOpen={setTopTokenListOpen}
                    setTokenAddress={setInnerTokenAddressTop}
                />
                <TokenModal
                    chainSymbol={chainSymbol}
                    account={address}
                    chainId={chainId}
                    listOpen={bottomTokenListOpen}
                    setListOpen={setBottomTokenListOpen}
                    setTokenAddress={setInnerTokenAddressBottom}
                />

                <Box sx={{ width: "100%", maxWidth: "420px", mt: "15px" }}>
                    <Paper
                        fullWidth
                        enableBackground
                        topRight={
                            <GhostStyledIcon
                                component={SettingsIcon}
                                viewBox="0 0 23 23"
                                style={{ display: "flex", alignItems: "center", cursor: "pointer" }}
                                onClick={handleSettingsOpen}
                            />
                        }
                        headerContent={
                            <Tabs
                                centered
                                textColor="primary"
                                indicatorColor="primary"
                                value={isSwap ? 0 : 1}
                                aria-label="Dex swap menu"
                                onChange={(_, view) => changeSwapTab(view === 0)}
                                TabIndicatorProps={{ style: { display: "none" } }}
                            >
                                <Tab aria-label="dex-swap-button" label="Swap" style={{ fontSize: "1.5rem" }} />
                                <Tab aria-label="dex-add-button" label="Pool" style={{ fontSize: "1.5rem" }} />
                            </Tabs>
                        }
                    >
                        <Box height="350px">
                            {isSwap ?
                                <SwapContainer
                                    tokenNameTop={tokenNameTop}
                                    tokenNameBottom={tokenNameBottom}
                                    onCardsSwap={onCardsSwap}
                                    chainId={chainId}
                                    address={address}
                                    dexAddresses={dexAddresses}
                                    connect={connect}
                                    slippage={slippage}
                                    destination={actualDestinationAddress ? actualDestinationAddress : address}
                                    secondsToWait={secondsToWait}
                                    setTopTokenListOpen={setTopTokenListOpen}
                                    setBottomTokenListOpen={setBottomTokenListOpen}
                                    setIsSwap={setIsSwap}
                                    formatDecimals={formatDecimals}
                                    isWrapping={isWrapping}
                                    isUnwrapping={isUnwrapping}
                                />
                                :
                                <PoolContainer
                                    tokenNameTop={tokenNameTop}
                                    tokenNameBottom={tokenNameBottom}
                                    onCardsSwap={onCardsSwap}
                                    chainId={chainId}
                                    address={address}
                                    dexAddresses={dexAddresses}
                                    connect={connect}
                                    slippage={slippage}
                                    destination={actualDestinationAddress ? actualDestinationAddress : address}
                                    secondsToWait={secondsToWait}
                                    setTopTokenListOpen={setTopTokenListOpen}
                                    setBottomTokenListOpen={setBottomTokenListOpen}
                                    formatDecimals={formatDecimals}
                                />
                            }
                        </Box>
                    </Paper>
                </Box>
            </Container>
        </Box>
    )
}

export default Dex;
