import type { ReduxStateType } from "@store/types";
import { useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";

import { ModalCloseBtn } from "@components/common/ModalHeader";
import { Sound } from "@components/common/Sound";
import { useFetchQuote } from "@hooks/apis/useFetchQuote";
import { useRequestStatusSSE } from "@hooks/apis/useRequestStatusSSE";
import { ScreenTab, setScreenTab } from "@store/globalSlice";
import { adjustDecimals } from "@utils/adjustDecimals";
import { NATIVE_TOKEN_ADDRESS } from "@utils/constants";
import { formatValue } from "@utils/formatValue";
import { getOrderStatus } from "@utils/getOrderStatus";
import {
	RequestStatusEnum,
	type STATUS_CODE,
	type StatusEndPoint,
	useRequestStatus,
} from "../../hooks/apis/useRequestStatus";
import { getRequestStatusData } from "../../utils/getRequestStatusData";
import { FAQ } from "../InFlight/FAQ";
import { AddTokenToWallet } from "./AddTokenToWallet";
import { Confetti } from "./Confetti";
import { Header } from "./Header";
import { InfoSection } from "./InfoSection";
import { RewardsBar } from "./RewardsBar";
import { RotatingText } from "./RotatingText";
import { Share } from "./Share";
import { SwapTime } from "./SwapTime";

export const InFlight = () => {
	const dispatch = useDispatch();
	const [isFaqOpen, toggleFaq] = useState<boolean>(false);
	const [isShareOpen, toggleShare] = useState<boolean>(false);
	const {
		requestHash,
		data: initialData,
		requestType,
	} = useSelector((state: ReduxStateType) => state.inflight);

	// To refetch the quote on screen closure
	const { refetch } = useFetchQuote();
	const paramAmount = useSelector(
		(state: ReduxStateType) => state.quoteParams.amounts,
	);

	const { data: updatedData } = useRequestStatus({
		requestHash: requestHash,
		requestType: requestType,
	});

	const {
		status: sseStatus,
		statusCode: sseStatusCode,
		fulfilAmount,
		srcTimestamp: sseSrcTimestamp,
		destTimestamp: sseDestTimestamp,
	} = useRequestStatusSSE({ requestHash });

	const [retainedSrcTimestamp, setRetainedSrcTimestamp] = useState<number>();
	// to store the src timestamp to local state
	useEffect(() => {
		if (sseSrcTimestamp) {
			setRetainedSrcTimestamp(sseSrcTimestamp);
		}
	}, [sseSrcTimestamp]);

	// Updating data with SSE data
	const latestData = useMemo(() => {
		if (
			sseStatus &&
			sseStatusCode &&
			updatedData &&
			sseStatusCode > updatedData?.statusCode
		) {
			const output = updatedData.output[0];
			const { isCompleted } = getOrderStatus(sseStatusCode);

			return {
				...updatedData,
				status: sseStatus,
				statusCode: sseStatusCode,
				output: [
					{
						...output,
						fulfilledAmount: fulfilAmount ?? "0",
						fulfilValueInUsd:
							output.fulfilValueInUsd || computeFulfilValueInUsd(output, fulfilAmount ?? "0"),
					},
				],
				srcTimestamp:
					sseSrcTimestamp ?? retainedSrcTimestamp ?? updatedData?.srcTimestamp,
				destTimestamp: sseDestTimestamp ?? updatedData?.destTimestamp,
			};
		}
		return updatedData;
	}, [
		updatedData,
		sseStatus,
		sseStatusCode,
		fulfilAmount,
		sseSrcTimestamp,
		sseDestTimestamp,
		retainedSrcTimestamp,
	]);

	const data = useMemo(
		() => latestData ?? initialData,
		[latestData, initialData],
	);

	const {
		swapTime,
		statusCode,
		input,
		output,
		primaryOutput,
		srcTimestamp,
		orderTimestamp,
		rebateDetails,
		quotedOutputInUsd,
	} = getRequestStatusData(data);
	const { isCompleted, isExpired, isPending } = getOrderStatus(statusCode);

	const [timestamp, setTimestamp] = useState<number>(
		Math.floor(Date.now() / 1000),
	);

	useEffect(() => {
		if (isCompleted) setTimestamp(swapTime);
		else if (srcTimestamp) setTimestamp(srcTimestamp);
		else if (orderTimestamp) setTimestamp(orderTimestamp);
	}, [isCompleted, swapTime, srcTimestamp, orderTimestamp]);

	const { isSurplusAmountReceived, surplusAmount } = useMemo((): {
		isSurplusAmountReceived: boolean;
		surplusAmount: number;
	} => {
		if (!primaryOutput?.fulfilValueInUsd || !quotedOutputInUsd)
			return { isSurplusAmountReceived: false, surplusAmount: 0 };

		const diff = primaryOutput.fulfilValueInUsd - quotedOutputInUsd;

		return { isSurplusAmountReceived: diff > 0, surplusAmount: diff };
	}, [primaryOutput, quotedOutputInUsd]);

	const completionSoundSource =
		"https://media.socket.tech/lite/completion-sound.wav";

	const isNativeToken =
		primaryOutput?.token?.address?.toLowerCase() === NATIVE_TOKEN_ADDRESS;

	return (
		<>
			<div className="bn-relative">
				<div className="bn-h-[120px] bn-flex bn-items-center bn-justify-center">
					<Header
						input={input}
						output={output}
						statusCode={statusCode}
						isSurplusAmountReceived={isSurplusAmountReceived}
					/>
				</div>

				<Sound
					source={completionSoundSource}
					play={isCompleted}
					classNames="bn-absolute bn-left-0 bn-top-0"
				/>

				<div className="bn-flex bn-flex-col bn-justify-center bn-w-full bn-mb-4">
					{!isExpired && !isPending && (
						<SwapTime
							label={timeLabel(statusCode)}
							time={requestType === "SWAP_REQUEST" && isCompleted ? 2 : timestamp}
							isCompleted={isCompleted}
						/>
					)}
					<h2 className="bn-text-center bn-text-xl bn-font-medium bn-leading-6 bn-text-primary bn-mb-1 bn-mt-2">
						{getTitle(statusCode, isSurplusAmountReceived ? surplusAmount : 0)}
					</h2>
					<RotatingText texts={getSubtitle(statusCode)} />
					{isCompleted && !isNativeToken && (
						<AddTokenToWallet token={primaryOutput.token} />
					)}
				</div>

				<InfoSection
					requestData={data}
					openFaq={() => toggleFaq(true)}
					openShare={() => toggleShare(true)}
				/>

				<ModalCloseBtn
					classNames="bn-absolute bn-right-0 bn-top-0"
					closeModal={() => {
						if (paramAmount?.[0]) refetch();
						dispatch(setScreenTab(ScreenTab.HOME));
					}}
				/>

				{/* {isCompleted && <Confetti />} */}
				<RewardsBar rebateDetails={rebateDetails} />
			</div>
			<FAQ isVisible={isFaqOpen} closePanel={() => toggleFaq(!isFaqOpen)} />
			<Share
				isVisible={isShareOpen}
				closePanel={() => toggleShare(!isShareOpen)}
				txHash={requestHash}
			/>
		</>
	);
};

function timeLabel(_statusCode: STATUS_CODE) {
	if (_statusCode < RequestStatusEnum.EXTRACTED || _statusCode === undefined)
		return "Searching Time";
	return "Swap Time";
}

function getTitle(statusCode: STATUS_CODE, surplusAmount: number) {
	const locale = useSelector((state: ReduxStateType) => state.settings.language);
	const { isPending, isCompleted, inProgress, isExpired } =
		getOrderStatus(statusCode);

	if (isExpired) return "Swap Request Expired";
	if (isPending) return "Finding the best price";
	if (inProgress) return "Swapping assets to wallet";

	if (isCompleted) {
		if (surplusAmount) {
			// biome-ignore format:
			return `Congrats! You got an extra $${adjustDecimals(
        surplusAmount,
        2,
        locale
      )}`;
		}
		return "Swap successfully completed";
	}

	return "Finding the best price";
}

const computeFulfilValueInUsd = (
	output: StatusEndPoint,
	fulfilAmount: string,
) => {
	if (output?.fulfilValueInUsd) return output.fulfilValueInUsd;

	if (fulfilAmount) {
		const formattedAmount = formatValue(
			fulfilAmount.toString(),
			output.token.decimals,
		);
		return output.priceInUsd * Number(formattedAmount);
	}

	return 0;
};

const pendingInfoTexts = [
	{ id: 0, text: "Your funds are safe in your wallet while we search" },
	{ id: 1, text: "Searching through all liquidity sources" },
	{ id: 2, text: "Finding the optimal path for your request" },
];

const inflightInfoTexts = [
	{ id: 0, text: "Your request has been picked up" },
	{ id: 1, text: "Processing your transaction onchain" },
	{ id: 2, text: "Almost there" },
	{ id: 3, text: "This usually takes a few moments" },
	{ id: 4, text: "More confirmations are required on some networks" },
];

function getSubtitle(_statusId: STATUS_CODE) {
	const { isPending, isCompleted, inProgress, isExpired } =
		getOrderStatus(_statusId);

	if (isPending) return pendingInfoTexts;
	if (inProgress) return inflightInfoTexts;
	if (isCompleted)
		return [{ id: 0, text: "Your swap has been confirmed on the blockchain." }];
	if (isExpired) {
		return [
			{
				id: 0,
				text:
					"We couldn't pick up for your request. Set a higher slippage and try again, Funds are safe in your wallet.",
			},
		];
	}

	return [pendingInfoTexts[0]];
}
