import { useQuery } from "@tanstack/react-query";
import { useMemo } from "react";
import { useSelector } from "react-redux";

import { useConfig } from "@hooks/useConfig";
import type { ReduxStateType } from "@store/types";
import { bungeeApiFetcher } from "@utils/fetcher";

interface Route {
	routeId: string;
	userTxs: Transaction[];
	totalGasFeesInUsd: string;
	serviceTime: number;
	maxServiceTime: number;
	inputValueInUsd: number;
	outputValueInUsd: number;
	receivedValueInUsd: number;
}

interface Transaction {
	userTxType: string;
	steps?: Step[];
	protocol?: string;
	fromAsset?: Asset;
	toAsset?: Asset;
	fromAmount?: string;
	toAmount?: string;
	swapSlippage?: number;
}

interface Step {
	type: string;
	protocol: string;
	fromAsset: Asset;
	toAsset: Asset;
	fromAmount: string;
	toAmount: string;
	swapSlippage?: number;
	bridgeSlippage?: number;
	fromChainId?: number;
	toChainId?: number;
}

interface Asset {
	address: string;
	symbol: string;
	decimals: number;
	logoURI: string;
}

type Protocol = {
	name: string;
	displayName: string;
	icon: string;
	securityScore?: number;
	robustnessScore?: number;
};

interface FormattedStep {
	type: "swap" | "bridge";
	protocol: Protocol | string;
	fromToken: Asset;
	toToken: Asset;
	fromAmount: string;
	toAmount: string;
	slippage?: number;
	fromChainId?: number;
	toChainId?: number;
}

export interface FormattedRoute {
	routeId: string;
	steps: FormattedStep[];
	totalGasFeesInUsd: string;
	serviceTime: number;
	maxServiceTime: number;
	totalFees: string;
	outputValueInUsd: string;
	protocol: Protocol;
	receivedValueInUsd: number;
}

export const useFetchV2Quote = () => {
	const { apiKey, baseUrl } = useConfig();
	const { fromChainId, fromTokens, toChainId, toTokens, sender, amounts } =
		useSelector((state: ReduxStateType) => state.quoteParams);

	// do not fetch quotes if there are multiple destination tokens
	const shouldFetch = Boolean(
		amounts?.length &&
			fromChainId &&
			toChainId &&
			fromTokens?.length &&
			toTokens?.length &&
			sender &&
			toTokens?.length < 2,
	);

	const queryParams = useMemo(() => {
		const params = new URLSearchParams({
			fromChainId: fromChainId?.toString() ?? "",
			toChainId: toChainId?.toString() ?? "",
			userAddress: sender ?? "",
			recipient: sender ?? "",
			uniqueRoutesPerBridge: "true",
			sort: "output",
			singleTxOnly: "false",
		});

		if (fromTokens?.length) {
			params.append("fromAmount", amounts[0]?.toString() ?? "");
			params.append("fromTokenAddress", fromTokens[0].address ?? "");
		}

		if (toTokens?.length) {
			params.append("toTokenAddress", toTokens[0].address ?? "");
		}

		return params;
	}, [fromChainId, toChainId, sender, fromTokens, toTokens, amounts]);

	// biome-ignore format:
	const query = useMemo(() => `${baseUrl}/quotes?${queryParams.toString()}`, [queryParams, baseUrl]);

	const { data, isLoading, isFetching, isError } = useQuery({
		queryKey: [
			"bungee-quote",
			apiKey,
			amounts?.[0],
			fromChainId,
			toChainId,
			fromTokens?.[0],
			toTokens?.[0],
			sender,
		],
		queryFn: () => bungeeApiFetcher(query, apiKey),
		enabled: shouldFetch,
		refetchOnMount: false,
		retry: 1,
		staleTime: 60000,
	});

	const formattedData = useMemo((): FormattedRoute[] | null => {
		if (!data?.result?.routes) return null;

		return data.result.routes.map((route: Route) => {
			const steps = route.userTxs
				.flatMap((tx: Transaction) => {
					if (tx.userTxType === "fund-movr") {
						return tx.steps
							?.map((step) => {
								if (step.type === "middleware") {
									return {
										type: "swap",
										protocol: step.protocol,
										fromToken: step.fromAsset,
										toToken: step.toAsset,
										fromAmount: step.fromAmount,
										toAmount: step.toAmount,
										slippage: step.swapSlippage,
									} as FormattedStep;
								}
								if (step.type === "bridge") {
									return {
										type: "bridge",
										protocol: step.protocol,
										fromToken: step.fromAsset,
										toToken: step.toAsset,
										fromAmount: step.fromAmount,
										toAmount: step.toAmount,
										slippage: step.bridgeSlippage,
										fromChainId: step.fromChainId,
										toChainId: step.toChainId,
									} as FormattedStep;
								}
								return null;
							})
							.filter((step): step is FormattedStep => step !== null);
					}
					if (tx.userTxType === "dex-swap") {
						return [
							{
								type: "swap",
								protocol: tx.protocol,
								fromToken: tx.fromAsset,
								toToken: tx.toAsset,
								fromAmount: tx.fromAmount,
								toAmount: tx.toAmount,
								slippage: tx.swapSlippage,
							} as FormattedStep,
						];
					}
					return null;
				})
				.filter((step): step is FormattedStep => step !== null);

			// Calculate total fees
			const totalGasFees = Number.parseFloat(route.totalGasFeesInUsd);
			const inputValueInUsd = route.inputValueInUsd;
			const outputValueInUsd = route.outputValueInUsd;

			const totalFees = inputValueInUsd - outputValueInUsd + totalGasFees;

			// Extract protocol details from the bridge step if available
			const protocol = steps.find((step) => step.type === "bridge")?.protocol ?? {};

			return {
				routeId: route.routeId,
				steps,
				totalGasFeesInUsd: route.totalGasFeesInUsd,
				serviceTime: route.serviceTime,
				maxServiceTime: route.maxServiceTime,
				totalFees,
				inputValueInUsd,
				outputValueInUsd,
				protocol,
				receivedValueInUsd: route.receivedValueInUsd,
			};
		});
	}, [data]);

	return {
		data: data?.result,
		formattedData: formattedData,
		isLoading,
		isRefetching: isFetching,
		noData: (shouldFetch && !isLoading && !data?.result) || isError,
	};
};
