"use client";
import {
	parseAsArrayOf,
	parseAsBoolean,
	parseAsInteger,
	parseAsString,
	parseAsStringLiteral,
	useQueryStates,
} from "nuqs";
import {
	createContext,
	type FC,
	type ReactNode,
	useCallback,
	useContext,
	useMemo,
	useTransition,
} from "react";

const sortOptions = ["Recenti", "Prezzo", "Consegna", "Mq"] as const;
export type SortTypes = (typeof sortOptions)[number];

type RicercaUrlParams = {
	map: boolean;
	tipo: string;
	comune: string;
	consegna: number[];
	minBudget: number;
	maxBudget: number;
	sort: SortTypes;
};

export const paramsMapping = {
	map: "m",
	tipo: "t",
	comune: "c",
	consegna: "f",
	minBudget: "bmin",
	maxBudget: "bmax",
	sort: "s",
} as const;

export const buildRicercaUrl = (params: Partial<RicercaUrlParams>): string => {
	const searchParams = new URLSearchParams();
	Object.entries(params).forEach(([key, value]) => {
		const mappedKey = paramsMapping[key as keyof typeof paramsMapping];
		if (value !== undefined && value !== null) {
			searchParams.set(mappedKey, value.toString());
		}
	});
	const queryString = searchParams.toString();
	return queryString ? `/annunci?${queryString}` : "/annunci";
};

type RicercaContextType = {
	map: boolean;
	tipo: RicercaUrlParams["tipo"];
	comune: RicercaUrlParams["comune"] | null;
	consegna: RicercaUrlParams["consegna"] | null;
	minBudget: RicercaUrlParams["minBudget"] | null;
	maxBudget: RicercaUrlParams["maxBudget"] | null;
	sort: SortTypes;

	setAllParams: (
		params: Partial<{
			tipo: RicercaUrlParams["tipo"];
			comune: RicercaUrlParams["comune"] | null;
			consegna: RicercaUrlParams["consegna"] | null;
			minBudget: RicercaUrlParams["minBudget"] | null;
			maxBudget: RicercaUrlParams["maxBudget"] | null;
			sort: RicercaUrlParams["sort"];
			map: RicercaUrlParams["map"];
		}>,
	) => Promise<URLSearchParams>;
	reset: () => void;

	comuniOptions: string[];
	tipologieOptions: string[];
	consegnaOptions: number[];
	sortOptions: typeof sortOptions;
	nFiltri: number;
	isLoading: boolean;
};

export type AnnunciOptions = {
	comune: string;
	tipo: string;
	consegna: number;
	prezzo: number;
}[];

const RicercaContext = createContext<RicercaContextType | undefined>(undefined);

export const useRicerca = () => {
	const context = useContext(RicercaContext);
	if (!context) {
		throw new Error("useRicerca must be used within a RicercaProvider");
	}
	return context;
};

export const RicercaProvider: FC<{
	children: ReactNode;
	options: AnnunciOptions;
}> = ({ children, options }) => {
	const [isLoading, startTransition] = useTransition();

	const [
		{ map, tipo, comune, consegna, minBudget, maxBudget, sort },
		setAllParams,
	] = useQueryStates(
		{
			map: parseAsBoolean.withDefault(false),
			tipo: parseAsString.withDefault(""),
			comune: parseAsString.withDefault(""),
			consegna: parseAsArrayOf(parseAsInteger),
			minBudget: parseAsInteger,
			maxBudget: parseAsInteger,
			sort: parseAsStringLiteral(sortOptions).withDefault("Recenti"),
		},
		{ startTransition, shallow: true, urlKeys: paramsMapping },
	);

	const reset = useCallback(() => {
		setAllParams({
			tipo: "",
			comune: "",
			consegna: null,
			minBudget: null,
			maxBudget: null,
			sort: "Recenti",
		});
	}, []);

	const { tipologieOptions, comuniOptions, consegnaOptions } = useMemo(() => {
		const typeSet = new Set<string>();
		const comuneSet = new Set<string>();
		const consegnaSet = new Set<number>();

		for (const item of options) {
			const matchesTipo = !tipo || item.tipo === tipo;
			const matchesComune = !comune || item.comune === comune;
			const matchesConsegna = !consegna || consegna.includes(item.consegna);
			const matchesBudget =
				(!minBudget || item.prezzo >= minBudget) &&
				(!maxBudget || item.prezzo <= maxBudget);

			// Add to Tipologie if it matches everything ELSE (Comune, Consegna, Budget)
			if (matchesComune && matchesConsegna && matchesBudget) {
				typeSet.add(item.tipo);
			}

			// Add to Comuni if it matches everything ELSE (Tipo, Consegna, Budget)
			if (matchesTipo && matchesConsegna && matchesBudget) {
				comuneSet.add(item.comune);
			}

			// Add to Consegna if it matches everything ELSE (Tipo, Comune, Budget)
			if (matchesTipo && matchesComune && matchesBudget) {
				consegnaSet.add(item.consegna);
			}
		}

		const currentMonth = new Date().getMonth() + 1; // 1-12

		return {
			tipologieOptions: Array.from(typeSet),
			comuniOptions: Array.from(comuneSet).sort((a, b) => a.localeCompare(b)),
			consegnaOptions: Array.from(consegnaSet).sort((a, b) => {
				const distA = (a - currentMonth + 12) % 12;
				const distB = (b - currentMonth + 12) % 12;
				return distA - distB;
			}),
		};
	}, [options, tipo, comune, consegna, minBudget, maxBudget]);

	const nFiltri =
		(tipo ? 1 : 0) +
		(comune ? 1 : 0) +
		(consegna !== null && consegna.length > 0 ? 1 : 0) +
		(sort !== "Recenti" ? 1 : 0) +
		(minBudget !== null ? 1 : 0) +
		(maxBudget !== null ? 1 : 0);

	return (
		<RicercaContext.Provider
			value={{
				map,
				tipo,
				comune,
				consegna,
				minBudget,
				maxBudget,
				sort,

				setAllParams,
				reset,

				comuniOptions,
				consegnaOptions,
				tipologieOptions,
				sortOptions,
				nFiltri,
				isLoading,
			}}
		>
			{children}
		</RicercaContext.Provider>
	);
};
