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

import { useConfig } from "@hooks/useConfig";
import type { ReduxStateType } from "@store/types";
import { bungeeApiFetcher } from "@utils/fetcher";
import { type FormattedQuoteData, getQuoteData } from "@utils/getQuoteData";
import type { Token } from "../../Providers/ConfigProvider";

function getTokenAddresses(tokens: Token[]) {
	return tokens?.map((token) => token.address);
}

function getMultiplePairs(key: string, values: string[] | number[]) {
	const _arr = [];

	for (const value of values) {
		// biome-ignore format:
		_arr.push(`${key}=${value}`);
	}

	return _arr.join("&");
}

export const useFetchQuote = () => {
	const { apiKey, baseUrl } = useConfig();
	const {
		fromChainId,
		fromTokens,
		toChainId,
		toTokens,
		sender,
		amounts,
		outputRatio,
		receiver
	} = useSelector((state: ReduxStateType) => state.quoteParams);
	const slippage = useSelector(
		(state: ReduxStateType) => state.settings.slippage,
	);
	const autoSlippage= useSelector((state: ReduxStateType) => state.settings.autoSlippage);

	// @fixme: should have validations for ratio and toTokens;
	// @note: amounts & inputToken length will have to be matched later
	const shouldFetch = !!(
		amounts?.length &&
		fromChainId &&
		toChainId &&
		fromTokens?.length &&
		toTokens?.length &&
		sender &&
		outputRatio?.length
	);

	// biome-ignore format:
	let query = `${baseUrl}/bungee-request/quote?originChainId=${fromChainId}&destinationChainId=${toChainId}&sender=${sender}&receiver=${sender}&compareRoutes=true`;

	// appending input amount & input token
	// note: multi input is not considered here
	if (fromTokens?.length) {
		// biome-ignore format:
		query += `&inputAmount=${amounts[0]}&inputToken=${fromTokens[0].address}`;
	}

	// appending output tokens
	if (toTokens?.length) {
		// biome-ignore format:
		query += `&${getMultiplePairs(
			"outputTokens",
			getTokenAddresses(toTokens)
		)}`;
	}

	// appending output ratio
	if (outputRatio) {
		// biome-ignore format:
		query += `&${getMultiplePairs("outputTokenRatio", outputRatio)}`;
	}

	if(receiver){
		query+=`&receiver=${receiver}`
	}

	if(autoSlippage) {
		query+='&useSuggestedSlippageForClient=true'
	} else {
		query+=`&outputSlippage=${slippage}`
	}

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

	const _slippage = autoSlippage ? data?.result?.suggestedSlippage  : slippage;

	return {
		data: getQuoteData(data?.result, _slippage) as FormattedQuoteData,
		isLoading,
		isRefetching: isFetching,
		refetch,
		noData: (shouldFetch && !isLoading && !data?.result) || isError,
	};
};

export interface EndPoint {
	token: Token;
	chainId: number;
	priceInUsd: number;
	amount: string;
	slippageAdjustedAmount?: string;
	valueInUsd?: number;
}

export interface BoosterItemType {
	id: string;
	label: string;
	description: string;
	icon: string;
}

export interface Boosters {
	coupons: BoosterItemType[];
}

export interface ComparisonRoute {
	bridgeName: string;
	displayName: string;
	serviceTime: number;
	icon: string;
	feesInUsd: number;
}

export type RequestType = "SWAP_REQUEST" | "SINGLE_OUTPUT_REQUEST";
export type TxData = {
	data: `0x${string}`;
	to: `0x${string}`;
	value: bigint;
};

export type RebateDetails = {
	token: Token,
	amount: string;
	chainId: number;
}
export interface Quote {
	requestType: RequestType;
	requestHash: string;
	deadline: number; // or string?
	sender: string;
	receiver: string;
	delegate: string;
	settler: string;
	switchboardId: string;
	nonce: number;
	input: EndPoint[];
	output: EndPoint[];
	affiliateFee: {
		affiliateFeeInBps: string;
		feeInBps: number;
		affiliateFeeTaker: string;
		token: Token;
		amount: string;
		chainId: number;
		valueInUsd: number;
		priceInUsd: number;
	};
	receiverExecution: {
		receiver: string;
		receiverCalldata: string;
		receiverMinGasLimit: string;
	};
	estimatedFulfilTime: number;
	suggestedSlippage: string;
	boosters?: Boosters;
	comparisionRoutes?: ComparisonRoute[];
	signTypedData: SignTypeData;
	txData?: TxData;
	rebateDetails: RebateDetails;
}

export interface SignTypeData {
	domain: {
		name: string;
		chainId: number;
		// biome-ignore format:
		verifyingContract: `0x${string}`;
	};
	types: {
		PermitWitnessTransferFrom: { name: string; type: string }[];
		TokenPermissions: { name: string; type: string }[];
		Request: { name: string; type: string }[];
		BasicRequest: { name: string; type: string }[];
		// New types for single output request
		SingleOutputRequest: { name: string; type: string }[];
		SwapRequest: { name: string; type: string }[];
	};
	values: {
		permitted: {
			token: string;
			amount: string;
		};
		spender: string;
		nonce: number;
		deadline: number;
		witness: {
			basicReq: {
				originChainId?: number; // Optional for swap request
				destinationChainId?: number; // Optional for swap request
				chainId?: number; // Optional for single output request
				deadline: number;
				nonce: number;
				sender: string;
				receiver: string;
				delegate?: string; // Optional for swap request
				bungeeGateway: string;
				switchboardId?: number; // Optional for single output request
				inputToken: string;
				inputAmount: string;
				outputToken?: string; // Optional for single output request
				outputTokens?: string[]; // Optional for swap request
				minOutputAmount?: string; // Optional for single output request
				minOutputAmounts?: string[]; // Optional for swap request
				refuelAmount?: string; // Optional for single output request
			};
			swapOutputToken?: string; // Optional for single output request
			minSwapOutput?: string; // Optional for single output request
			exclusiveTransmitters?: string[]; // Optional for swap request
			minDestGas?: number; // Optional for swap request
			destinationPayload?: string; // Optional for swap request
			metadata: string;
			affiliateFees: string;
		};
	};
}
