import { useEffect, useRef, useState } from "react";
import { useSelector } from "react-redux";

import { useLogEvent } from "@hooks/useLogEvent";
import { clearActiveRoute, setActiveRoute } from "@store/routesSlice";
import type { ReduxStateType } from "@store/types";
import { isWrapUnwrapCase } from "@utils/isWrapUnwrapCase";
import { useDispatch } from "react-redux";
import { onQuoteResult } from "../../../../analytics/events";
import {
	type FormattedManualQuote,
	type FormattedQuoteResponse,
	ROUTE_TAG,
} from "./types";
import { useActiveRoute } from "./useActiveRoute";
import { useTopRoutes } from "./useTopRoutes";
import { isAutoQuote, isManualQuote } from "./utils";

/**
 * Route Selection Logic
 *
 * First quote call (activeRoute === undefined):
 *   - Set bestRoute as active route (prioritizes suggestedRoute for optimal default)
 *
 * Quote refresh (activeRoute exists):
 *   - If retainPreviousRoute is false: Always select bestRoute
 *   - If retainPreviousRoute is true (default):
 *     - Check activeRoute tags against available routes (MAX_OUTPUT > FASTEST > SUGGESTED priority)
 *     - Priority rationale: On first call, we show suggested route (most optimal) by default.
 *       On subsequent refreshes, we retain explicit user choices (MAX_OUTPUT/FASTEST) over the
 *       default suggestion. This ensures users who selected max output or fastest routes keep
 *       their preference, while still allowing the suggested route to update on refresh.
 *     - If activeRoute doesn't match any top route and it's manual -> try protocol matching
 *     - Otherwise fallback to bestRoute
 *
 * Mode switch:
 *   - Clear active route (handled in separate effect)
 *
 * Wrap/unwrap case:
 *   - Always use autoRoute (takes priority)
 */

interface UseRouteSelectionOptions {
	retainPreviousRoute?: boolean;
}

export const useRouteSelection = (options: UseRouteSelectionOptions = {}) => {
	const { retainPreviousRoute = true } = options;
	const dispatch = useDispatch();
	const { logEvent } = useLogEvent();

	const [selectedRoute, setSelectedRoute] = useState<FormattedQuoteResponse>();

	const { data: activeRoute } = useActiveRoute();
	const { auto, manual, formattedAutoRoutes, formattedManualRoutes } =
		useTopRoutes();

	const routingMode = useSelector(
		(state: ReduxStateType) => state.settings.routingMode,
	);
	const lastDispatchedQuoteId = useRef<string | null>(null);
	const previousRoutingMode = useRef<"auto" | "manual">(routingMode);

	// Get routes based on routingMode
	const suggestedRoute =
		routingMode === "auto" ? auto.suggestedRoute : undefined;
	const maxOutputRouteForMode =
		routingMode === "auto" ? auto.maxOutputRoute : manual.maxOutputRoute;
	const fastestRouteForMode =
		routingMode === "auto" ? auto.fastestRoute : manual.fastestRoute;
	const routeWithSuggestedTag =
		routingMode === "auto" ? auto.routeWithSuggestedTag : undefined;
	const autoRoute = formattedAutoRoutes?.[0];
	const manualRoutes = formattedManualRoutes || undefined;

	// Calculate bestRoute: route with SUGGESTED tag > suggestedRoute > max > fastest
	// The routeWithSuggestedTag is tracked in useTopRoutes and ensures that even if a route
	// is categorized as fastestRoute or maxOutputRoute, if it has the SUGGESTED tag, it will
	// be selected as the default route.
	const bestRoute =
		routeWithSuggestedTag ||
		suggestedRoute ||
		maxOutputRouteForMode ||
		fastestRouteForMode ||
		undefined;

	// Handle mode switch - clear active route when mode changes
	useEffect(() => {
		if (previousRoutingMode.current !== routingMode) {
			previousRoutingMode.current = routingMode;
			dispatch(clearActiveRoute());
			setSelectedRoute(undefined);
		}
	}, [routingMode, dispatch]);

	// Main route selection logic
	useEffect(() => {
		let newRoute: FormattedQuoteResponse | undefined;

		// Wrap/unwrap case takes priority - check all auto routes
		const wrapUnwrapRoute = formattedAutoRoutes?.find((route) =>
			isWrapUnwrapCase(route),
		);
		if (wrapUnwrapRoute) {
			newRoute = wrapUnwrapRoute;
		} else if (!activeRoute) {
			// First quote call - no active route exists
			newRoute = bestRoute;
		} else if (!retainPreviousRoute) {
			// Quote refresh - retention is disabled, always select best route
			newRoute = bestRoute;
		} else {
			// Quote refresh - try to retain previous route preference
			const retainedRoute = getRetainedRoute(
				activeRoute,
				suggestedRoute,
				maxOutputRouteForMode,
				fastestRouteForMode,
				manualRoutes,
			);
			newRoute = retainedRoute || bestRoute;
		}

		// Only update state if the route actually changed (compare by quoteId)
		const newQuoteId = newRoute?.quoteId;
		if (selectedRoute?.quoteId !== newQuoteId) {
			setSelectedRoute(newRoute);
		}
	}, [
		bestRoute,
		activeRoute,
		suggestedRoute,
		maxOutputRouteForMode,
		fastestRouteForMode,
		formattedAutoRoutes,
		manualRoutes,
		retainPreviousRoute,
		selectedRoute?.quoteId,
	]);

	useEffect(() => {
		// Emit event
		const quoteCount = [...(manualRoutes || []), autoRoute].filter(
			Boolean,
		).length;
		if (
			selectedRoute &&
			selectedRoute.quoteId !== lastDispatchedQuoteId.current
		) {
			logEvent("quote_result", {
				suggested_option: suggestedRoute,
				max_output_option: maxOutputRouteForMode,
				fastest_option: fastestRouteForMode,
				default_selected: selectedRoute,
				quote_count: quoteCount,
				status: quoteCount > 0 ? "success" : "no_quotes_returned",

				source_chain_id: selectedRoute?.primaryInput?.token?.chainId,
				destination_chain_id: selectedRoute?.primaryOutput?.token?.chainId,
				source_token_symbol: selectedRoute?.primaryInput?.token?.symbol,
				destination_token_symbol: selectedRoute?.primaryOutput?.token?.symbol,
				input_amount: selectedRoute?.primaryInput?.amount
					? Number(selectedRoute?.primaryInput?.amount)
					: undefined,

				input_amount_usd: selectedRoute?.primaryInput?.valueInUsd
					? Number(selectedRoute?.primaryInput.valueInUsd)
					: undefined,

				refuel: selectedRoute?.refuel,
			});
		}
		onQuoteResult({
			fastest_option: fastestRouteForMode,
			default_selected: selectedRoute,
			quote_count: quoteCount,
			status: quoteCount > 0 ? "success" : "no_quotes_returned",

			source_chain_id: selectedRoute?.primaryInput?.token?.chainId,
			destination_chain_id: selectedRoute?.primaryOutput?.token?.chainId,
			source_token_symbol: selectedRoute?.primaryInput?.token?.symbol,
			destination_token_symbol: selectedRoute?.primaryOutput?.token?.symbol,
			input_amount: selectedRoute?.primaryInput?.amount
				? Number(selectedRoute?.primaryInput?.amount)
				: undefined,
			bestoutput_option: maxOutputRouteForMode,
		});

		if (
			selectedRoute &&
			selectedRoute.quoteId !== lastDispatchedQuoteId.current
		) {
			lastDispatchedQuoteId.current = selectedRoute.quoteId;
			dispatch(setActiveRoute(selectedRoute));
		} else if (!selectedRoute && lastDispatchedQuoteId.current !== null) {
			lastDispatchedQuoteId.current = null;
			dispatch(setActiveRoute(undefined));
		}
	}, [
		selectedRoute,
		dispatch,
		suggestedRoute,
		maxOutputRouteForMode,
		fastestRouteForMode,
		manualRoutes,
		autoRoute,
		logEvent,
	]);
};

/**
 * Helper function to get retained route based on activeRoute tags and protocol matching
 * Priority: MAX_OUTPUT > FASTEST > SUGGESTED > Protocol matching (for manual routes)
 */
const getRetainedRoute = (
	activeRoute: FormattedQuoteResponse,
	suggestedRoute: FormattedQuoteResponse | undefined,
	maxOutputRouteForMode: FormattedQuoteResponse | undefined,
	fastestRouteForMode: FormattedQuoteResponse | undefined,
	manualRoutes: FormattedManualQuote[] | undefined,
): FormattedQuoteResponse | undefined => {
	// Check activeRoute tags to determine which category to retain
	// Priority: MAX_OUTPUT > FASTEST > SUGGESTED
	const activeRouteTags = isAutoQuote(activeRoute)
		? activeRoute.routeTags || []
		: [];

	// Priority-based tag matching
	if (activeRouteTags.includes(ROUTE_TAG.MAX_OUTPUT) && maxOutputRouteForMode) {
		return maxOutputRouteForMode;
	}

	if (activeRouteTags.includes(ROUTE_TAG.FASTEST) && fastestRouteForMode) {
		return fastestRouteForMode;
	}

	if (activeRouteTags.includes(ROUTE_TAG.SUGGESTED) && suggestedRoute) {
		return suggestedRoute;
	}

	// If activeRoute doesn't match any top route and it's a manual route, try protocol matching
	if (isManualQuote(activeRoute)) {
		const filteredManualRoute = manualRoutes?.find(
			(route) => route.protocol.name === activeRoute.protocol.name,
		);
		return filteredManualRoute;
	}

	return undefined;
};
