import { AnimatePresence, motion } from "framer-motion";
import React, { type ReactNode, useEffect, useMemo, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";

import { chainColors } from "@components/TokenItem";
import { Button } from "@components/common/Button";
import { ModalCloseBtn } from "@components/common/ModalHeader";
import { Tooltip } from "@components/common/Tooltip";
import { type EndPoint, useFetchQuote } from "@hooks/apis/useFetchQuote";
import { useMappedChainData } from "@hooks/useMappedChainData";
import { useTheme } from "@hooks/useTheme";
import { ScreenTab, setScreenTab } from "@store/globalSlice";
import type { ReduxStateType } from "@store/types";
import { adjustDecimals } from "@utils/adjustDecimals";
import { formatValue } from "@utils/formatValue";
import { getBorderRadius } from "@utils/getBorderRadius";
import type { FormattedQuoteData } from "@utils/getQuoteData";
import { TokenDisplay } from "../common/TokenDisplay";
import { useActionStatusContext } from "./ActionStatusProvider";
import { ReviewActionButton } from "./ReviewActionButton";
import { ReviewSection } from "./ReviewSection";

export const Review = () => {
	const dispatch = useDispatch();
	const { data: newQuoteData } = useFetchQuote();
	const {status: actionStatus} = useActionStatusContext();

	const [quoteData, setQuoteData] = useState<FormattedQuoteData>();
	const [isQuoteUpdated, setIsQuoteUpdated] = useState<boolean>(false);

	const firstRender = useRef(true);

	// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
	useEffect(() => {
		if (!quoteData && firstRender.current && newQuoteData) {
			setQuoteData(newQuoteData);
			firstRender.current = false;
		}

		if (quoteData && newQuoteData) {
			setIsQuoteUpdated(quoteData?.deadline !== newQuoteData?.deadline);
		}
	}, [newQuoteData]);

	const { primaryOutput, input, output } = quoteData || {};

	function closeReview() {
		dispatch(setScreenTab(ScreenTab.HOME));
	}

	function acceptNewPrice() {
		setQuoteData(newQuoteData);
		setIsQuoteUpdated(false);
	}

	const title = useGetTitle(input, output);
	const priceDiffInPercentage = useMemo(() => {
		if(primaryOutput && newQuoteData){
			const {primaryOutput:primaryOutputNew} = newQuoteData;
			if(primaryOutput?.valueInUsd && primaryOutputNew?.valueInUsd){
				return ((primaryOutputNew.valueInUsd - primaryOutput.valueInUsd) / primaryOutput.valueInUsd) * 100;
			}
		}
	}, [primaryOutput, newQuoteData])

	return (
		<AnimatePresence>
			<motion.div
				layout
				initial={{ opacity: 0, y: 20 }}
				animate={{ opacity: 1, y: 0 }}
				exit={{ opacity: 0, y: -20 }}
				transition={{ duration: 0.3, ease: "easeInOut" }}
			>
				<div className="bn-h-6 bn-flex bn-items-center bn-justify-center bn-relative bn-mb-4">
					<p className="bn-text-primary bn-text-lg bn-font-medium">Review</p>

					<ModalCloseBtn closeModal={closeReview} classNames="bn-absolute bn-right-0" />
				</div>

				<motion.div
					initial={{ opacity: 0, scale: 0.9 }}
					animate={{ opacity: 1, scale: 1 }}
					transition={{ delay: 0.1, duration: 0.2 }}
					className="bn-mb-4 bn-w-fit bn-mx-auto"
				>
					{input && output && <TokenDisplay input={input} output={output} />}
				</motion.div>

				<motion.div
					initial={{ opacity: 0, y: 10 }}
					animate={{ opacity: 1, y: 0 }}
					transition={{ delay: 0.2, duration: 0.2 }}
				>
					<p className="bn-text-secondary bn-font-normal bn-font-base bn-text-center bn-mb-1">
						You're Swapping
					</p>
					<p className="bn-text-lg bn-font-medium bn-aeonik-font bn-text-center bn-text-primary">
						{title}
					</p>
				</motion.div>

				<motion.div
					initial={{ opacity: 0, scale: 0.95 }}
					animate={{ opacity: 1, scale: 1 }}
					transition={{ delay: 0.3, duration: 0.2 }}
					className="bn-p-[14px] bn-border bn-border-dashed bn-border-primary bn-rounded-xl bn-my-4"
				>
					<ReviewSection full />
				</motion.div>

				<motion.div
					initial={{ opacity: 0, y: 10 }}
					animate={{ opacity: 1, y: 0 }}
					transition={{ duration: 0.2 }}
				>
					<QuoteUpdated
						isQuoteUpdated={isQuoteUpdated && actionStatus !== "awaiting_wallet_confirmation" && actionStatus !== "loading"}
						endpoint={primaryOutput}
						acceptNewPrice={acceptNewPrice}
						priceDiffInPercentage={priceDiffInPercentage}
					>
						{quoteData && <ReviewActionButton quoteData={quoteData} />}
					</QuoteUpdated>
				</motion.div>
			</motion.div>
		</AnimatePresence>
	);
};

const QuoteUpdated = ({
	children,
	endpoint,
	isQuoteUpdated = false,
	acceptNewPrice,
	priceDiffInPercentage
}: {
	children: ReactNode;
	endpoint?: EndPoint;
	isQuoteUpdated: boolean;
	acceptNewPrice: () => void;
	priceDiffInPercentage?: number;
}) => {
	const { borderRadius } = useTheme();
	const token = endpoint?.token;
	const outputAmount = token && formatValue(endpoint.amount, token.decimals);

	// biome-ignore format:
	const containerStyle = `${isQuoteUpdated ? "bn-bg-layer-2" : ""}`;

	return (
		<motion.div
			layout
			initial={{ opacity: 0, scale: 0.9, padding: 0 }}
			animate={{ opacity: 1, scale: 1, padding: isQuoteUpdated ? "14px" : 0 }}
			exit={{ opacity: 0, scale: 0.9, padding: 0 }}
			transition={{ duration: 0.3 }}
			className={containerStyle}
			style={{ borderRadius: getBorderRadius(borderRadius, 8) }}
		>
			{isQuoteUpdated && outputAmount ? (
				<motion.div
					initial={{ opacity: 0, y: -10 }}
					animate={{ opacity: 1, y: 0 }}
					exit={{ opacity: 0, y: -10 }}
					transition={{ duration: 0.3, delay: 0.3 }}
				>
					<p className="bn-text-[13px] bn-font-normal bn-text-secondary bn-mb-1.5">
						Quote updated:{" "}
					</p>
					<p className="bn-text-base bn-font-medium bn-aeonik-font bn-text-secondary bn-mb-3 bn-space-x-1.5">
						<span className="bn-text-primary">
							<Tooltip content={outputAmount}>
								<span>{adjustDecimals(outputAmount, 3)}</span>
							</Tooltip>{" "}
							{token?.symbol}
						</span>
						{endpoint?.valueInUsd && <span>(${adjustDecimals(endpoint.valueInUsd)})</span>}
						{/* biome-ignore format: */}
						{priceDiffInPercentage ? <span className={`${priceDiffInPercentage > 0 ? 'bn-text-success' : 'bn-text-fail'}`}>{priceDiffInPercentage.toFixed(5)}%</span>: null}
					</p>
					<Button onClick={acceptNewPrice} label="Accept New Price" />
				</motion.div>
			) : (
				children
			)}
		</motion.div>
	);
};

function getTokenDetail(endpoint: EndPoint[], locale: string) {
	return endpoint.map((_endpoint, index) => {
		const token = _endpoint.token;
		const formattedValue = formatValue(_endpoint.amount, token.decimals);

		// biome-ignore format:
		const tokenValueInText = `${adjustDecimals(formattedValue, 3, locale)} ${token.symbol}`;

		if (index === 0) {
			return tokenValueInText;
		}

		// biome-ignore lint/style/useTemplate: <explanation>
		return " & " + tokenValueInText;
	});
}

function useGetTitle(input?: EndPoint[], output?: EndPoint[]) {
	const mappedChainData = useMappedChainData();
	const locale = useSelector((state: ReduxStateType) => state.settings.language);

	if (!input || !output) return;

	const inputToken = input[0].token;
	const outputToken = output[0].token;

	const inputColor = chainColors[input[0].chainId];
	const outputColor = chainColors[output[0].chainId];

	const inputChainName = mappedChainData?.[inputToken?.chainId]?.name;
	const outputChainName = mappedChainData?.[outputToken?.chainId]?.name;

	return (
		<>
			<span className={inputColor}>{getTokenDetail(input, locale)}</span> on{" "}
			{inputChainName} to receive{" "}
			<span className={outputColor}>{getTokenDetail(output, locale)}</span> on{" "}
			{outputChainName}
		</>
	);
}
