import { useMemo } from "react";

import { areBothRoutesSame } from "@utils/areBothRoutesSame";
import {
	type FormattedAutoQuote,
	type FormattedManualQuote,
	ROUTE_TAG,
} from "./types";
import { useFetchQuotes } from "./useFetchQuotes";

export const useTopRoutes = () => {
	const {
		formattedAutoRoutes,
		formattedManualRoutes,
		refetch,
		isRefetchIntervalPaused,
	} = useFetchQuotes();

	const topRoutes = useMemo(() => {
		const auto: {
			fastestRoute?: FormattedAutoQuote;
			maxOutputRoute?: FormattedAutoQuote;
			suggestedRoute?: FormattedAutoQuote;
			routeWithSuggestedTag?: FormattedAutoQuote;
		} = {};

		const manual: {
			fastestRoute?: FormattedManualQuote;
			maxOutputRoute?: FormattedManualQuote;
			restRoutes?: FormattedManualQuote[];
		} = {};

		// Process auto routes
		if (formattedAutoRoutes?.length) {
			let fallbackRoute: FormattedAutoQuote | undefined;

			for (const route of formattedAutoRoutes) {
				const routeTags = route.routeTags;

				// If no tags, keep as fallback but continue processing
				if (!routeTags || routeTags.length === 0) {
					if (!fallbackRoute) {
						fallbackRoute = route;
					}
					continue;
				}

				const hasMaxOutput = routeTags.includes(ROUTE_TAG.MAX_OUTPUT);
				const hasFastest = routeTags.includes(ROUTE_TAG.FASTEST);
				const hasSuggested = routeTags.includes(ROUTE_TAG.SUGGESTED);

				// Track route with SUGGESTED tag (regardless of other tags) for default selection
				// Priority: first route with SUGGESTED tag found (typically maxOutputRoute or fastestRoute if they have it)
				if (hasSuggested && !auto.routeWithSuggestedTag) {
					auto.routeWithSuggestedTag = route;
				}

				// Priority: MAX_OUTPUT > FASTEST > SUGGESTED
				// If a route has multiple tags (e.g., MAX_OUTPUT + SUGGESTED), we categorize it
				// by the highest priority tag. This ensures that if a max route is also the
				// suggested route, we show it as max route. Suggested route should only appear
				// when it's not max or fastest route.
				if (hasMaxOutput) {
					// If has MAX_OUTPUT, assign to maxOutputRoute (highest priority)
					auto.maxOutputRoute = route;
				} else if (hasFastest) {
					// If has FASTEST (but not MAX_OUTPUT), assign to fastestRoute
					auto.fastestRoute = route;
				} else if (hasSuggested) {
					// If only has SUGGESTED, assign to suggestedRoute
					auto.suggestedRoute = route;
				}
			}

			// If no routes with tags were found, use fallback route as maxOutputRoute
			if (
				!auto.suggestedRoute &&
				!auto.maxOutputRoute &&
				!auto.fastestRoute &&
				fallbackRoute
			) {
				auto.maxOutputRoute = fallbackRoute;
			}
		}

		// Process manual routes
		if (formattedManualRoutes?.length) {
			// Find fastest route (minimum estimatedFulfilTime)
			const fastestManualRoute = formattedManualRoutes.reduce(
				(fastest, route) => {
					if (!fastest) return route;
					return route.estimatedFulfilTime < fastest.estimatedFulfilTime
						? route
						: fastest;
				},
				undefined as FormattedManualQuote | undefined,
			);

			// Find max output route (maximum effectiveReceivedInUsd or valueInUsd)
			const maxOutputManualRoute = formattedManualRoutes.reduce(
				(max, route) => {
					if (!max) return route;
					const routeOutput =
						route.primaryOutput?.effectiveReceivedInUsd ??
						route.primaryOutput?.valueInUsd ??
						0;
					const maxOutput =
						max.primaryOutput?.effectiveReceivedInUsd ??
						max.primaryOutput?.valueInUsd ??
						0;
					return routeOutput > maxOutput ? route : max;
				},
				undefined as FormattedManualQuote | undefined,
			);

			manual.fastestRoute = fastestManualRoute;
			manual.maxOutputRoute = maxOutputManualRoute;

			// Check for duplicates in manual routes (priority: MAX_OUTPUT > FASTEST)
			// If fastest and max are the same, keep only maxOutputRoute
			if (
				manual.fastestRoute &&
				manual.maxOutputRoute &&
				areBothRoutesSame(manual.fastestRoute, manual.maxOutputRoute)
			) {
				manual.fastestRoute = undefined;
			}

			// Get rest routes (excluding fastest and max output, sorted by output descending)
			const excludedIds = new Set(
				[fastestManualRoute?.quoteId, maxOutputManualRoute?.quoteId].filter(
					Boolean,
				),
			);

			const restRoutes = formattedManualRoutes
				.filter((route) => !excludedIds.has(route.quoteId))
				.sort((a, b) => {
					const aOutput =
						a.primaryOutput?.effectiveReceivedInUsd ??
						a.primaryOutput?.valueInUsd ??
						0;
					const bOutput =
						b.primaryOutput?.effectiveReceivedInUsd ??
						b.primaryOutput?.valueInUsd ??
						0;
					return bOutput - aOutput; // Descending order
				});

			if (restRoutes.length > 0) {
				manual.restRoutes = restRoutes;
			}
		}

		return { auto, manual };
	}, [formattedAutoRoutes, formattedManualRoutes]);

	return {
		...topRoutes,
		formattedAutoRoutes,
		formattedManualRoutes,
		refetch,
		isRefetchIntervalPaused,
	};
};
