import { useEffect, useMemo, useRef } from "react";
import { useSelector } from "react-redux";
import { useCallsStatus } from "wagmi";

import { RequestStatusEnum, type STATUS_CODE } from "@/types/index";
import type { AutoOrderData } from "@hooks/apis/auto/useAutoHistory";
import { useConfig } from "@hooks/useConfig";
import { useLogEvent } from "@hooks/useLogEvent";
import type { ReduxStateType } from "@store/types";
import {
	type TrackOrderStatus,
	onCompleteSwap,
	trackOrderStatus,
} from "../../../analytics/events";
import { trackOnceIfNotAlready } from "../../../analytics/trackOnceIfNotAlready";
import { AutoData, type InflightInfo } from "../common/data";
import { useUpdateAutoStatus } from "../common/useUpdateAutoStatus";
import { normalizeRouteData } from "../common/utils";
import { InFlightLayout } from "../layout";
import { deriveCallsStatus } from "./ManualInFlight";

export const AutoInflight = () => {
	const { postSwapAction } = useConfig();
	const { data: initialData } = useSelector(
		(state: ReduxStateType) => state.inflight,
	);

	const { logEvent } = useLogEvent();

	const routeData = useUpdateAutoStatus({
		initialData: initialData as AutoOrderData,
	});

	const isBatchingFlow = initialData?.flowType === "bundled";
	const shouldStopPolling = useRef(false);

	const { data: callsData, error: callsStatusError } = useCallsStatus({
		id: initialData?.batchId as string,
		query: {
			refetchInterval: shouldStopPolling.current ? false : 3000,
			retry: (value) => {
				if (value > 1) {
					shouldStopPolling.current = true;
					return false;
				}
				return true;
			},
			enabled:
				isBatchingFlow &&
				!!initialData?.batchId &&
				routeData?.status !== "COMPLETED" &&
				routeData?.status !== "SETTLED" &&
				!shouldStopPolling.current,
		},
	});

	const effectiveCallsStatus = deriveCallsStatus(callsData);

	// only updating the status in case of failure
	// status api (useUpdateAutoStatus) handles the success status
	const updatedRouteData = useMemo(() => {
		// biome-ignore lint/style/noNonNullAssertion: <explanation>
		const data = normalizeRouteData(routeData || initialData!);
		let status: keyof typeof RequestStatusEnum | undefined = data?.status;
		let statusCode: STATUS_CODE | undefined = data?.statusCode;

		if (effectiveCallsStatus === "reverted" || callsStatusError) {
			status = "REVERTED";
			statusCode = RequestStatusEnum.REVERTED;
		}

		return {
			...data,
			status,
			statusCode,
		};
	}, [effectiveCallsStatus, callsStatusError, initialData, routeData]);

	const data: InflightInfo = useMemo(
		() => ({
			title: AutoData.title,
			subTitle: AutoData.subTitle,
			info: updatedRouteData as AutoOrderData,
		}),
		[updatedRouteData],
	);

	useEffect(() => {
		if (!routeData) return;

		const isFinal =
			routeData.status === "COMPLETED" || routeData.status === "REVERTED";
		if (!isFinal) return;

		const fromToken = routeData.primaryInput.token;
		const toToken = routeData.primaryOutput.token;
		const isSameSwap = fromToken?.chainId === toToken?.chainId;
		const routingMode = `${routeData.routingMode}-${
			isSameSwap ? "same-chain" : "cross-chain"
		}`;

		const orderData: TrackOrderStatus = {
			status: routeData.status === "COMPLETED" ? "success" : "failed",
			from_token: fromToken.symbol,
			to_token: toToken.symbol,
			from_chain: fromToken.chainId,
			to_chain: toToken.chainId,
			routingMode: routingMode,
			flowType: routeData?.batchId ? "bundled" : "regular",
			// note: we don't get slippage data in history api, so this data won't be accurate
			slippage: routeData?.slippage || undefined,
			suggestedClientSlippage: Boolean(routeData?.suggestedClientSlippage),
		};

		trackOnceIfNotAlready({
			type: "auto",
			requestHash: routeData.requestHash,
			trackFn: () => {
				trackOrderStatus(orderData);

				onCompleteSwap(
					{
						status: orderData.status,
						request_hash: routeData.requestHash,
						provider_name: "",
						actual_time_seconds: routeData.orderTimestamp,
						actual_output_amount: Number(routeData.primaryOutput.amount),
						estimated_time_seconds: +new Date(routeData.orderTimestamp) / 1000,
						estimated_output_amount: Number(routeData.primaryOutput.amount),
					},
					logEvent,
				);

				if (postSwapAction?.onSwapCompleted) {
					postSwapAction.onSwapCompleted(routeData.requestHash);
				}
			},
		});
	}, [routeData, logEvent, postSwapAction?.onSwapCompleted]);

	return <InFlightLayout data={data} />;
};
