import { keepPreviousData } from "@tanstack/react-query";
import {
	ChevronDown,
	Filter,
	FilterX,
	MapIcon,
	RotateCcwIcon,
	Table,
} from "lucide-react";
import type { GetServerSideProps } from "next";
import dynamic from "next/dynamic";
import Head from "next/head";
import { useCallback, useEffect, useMemo, useState } from "react";
import { AccordionComp } from "~/components/accordionComp";
import { CTA_TipologiaModal } from "~/components/annunci_tutorial";
import { LazyCardAnnuncio } from "~/components/annuncio_card";
import { CodiceBox } from "~/components/codiceRicerca";
import { Layout } from "~/components/Layout";
import { LoadingPage } from "~/components/loading";
import ScrollToTop from "~/components/scroll_top";
import { Status500 } from "~/components/status-page";
import { Badge } from "~/components/ui/badge";
import { Button } from "~/components/ui/button";
import {
	DropdownMenu,
	DropdownMenuContent,
	DropdownMenuTrigger,
} from "~/components/ui/dropdown-menu";
import { Label } from "~/components/ui/label";
import {
	Select,
	SelectContent,
	SelectItem,
	SelectTrigger,
	SelectValue,
} from "~/components/ui/select";
import { Slider } from "~/components/ui/slider";
import { useMediaQuery } from "~/hooks/use-media-query";
import { cn, debounce } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider";
import {
	type AnnunciOptions,
	type FilterOption,
	RicercaProvider,
	type SortTypes,
	useRicerca,
} from "~/providers/RicercaProvider";
import { generateSSGHelper } from "~/server/utils/ssgHelper";
import { api } from "~/utils/api";

const MapSection = dynamic(
	() =>
		import("~/components/annunci_map").then((mod) => ({
			default: mod.MapSection,
		})),
	{
		ssr: false,
		loading: () => <LoadingPage />,
	},
);

type AnnunciPageProps = {
	options: AnnunciOptions;
};

/**
 * Pagina annunci: /annunci
 */
const Annunci = ({ options }: AnnunciPageProps) => {
	const { t } = useTranslation();

	return (
		<RicercaProvider options={options}>
			<Head>
				<title>{t.heads.annunci_titolo}</title>
				<meta content={t.heads.annunci_description} name="description" />
			</Head>

			<main className="relative mx-auto w-full max-w-500 space-y-5 bg-background px-2 py-5 md:px-8">
				<Badge className="bg-muted py-1 text-foreground text-sm outline outline-muted-foreground">
					{t.annunci.titolo}
				</Badge>
				<ScrollToTop />
				<Ricerca />
				<AccordionComp
					className="max-w-6xl px-4 py-4 md:py-8"
					defaultOpen={0}
					texts={t.annunci.accordion_minifaq}
				/>
			</main>
		</RicercaProvider>
	);
};

export default Annunci;

Annunci.getLayout = (page: React.ReactNode) => {
	return <Layout>{page}</Layout>;
};

export const getServerSideProps = (async () => {
	const helper = generateSSGHelper();
	const options = await helper.annunci.getAnnunciOptions.fetch();
	return {
		props: {
			options,
		},
	};
}) satisfies GetServerSideProps<AnnunciPageProps>;

const Ricerca = () => {
	const { map, isLoading } = useRicerca();

	return (
		<div className="mx-auto w-full space-y-5">
			<RicercaHeader />
			<div className="relative flex w-full gap-4">
				<RicercaSideFilters />

				<div className="flex-1">
					{isLoading ? <LoadingPage /> : map ? <MapSection /> : <AnnunciList />}
				</div>
			</div>
		</div>
	);
};

const sort_options: FilterOption<SortTypes>[] = [
	{ value: "Recenti", disabled: false },
	{ value: "Prezzo", disabled: false },
	{ value: "Consegna", disabled: false },
	{ value: "Mq", disabled: false },
];

const Filters = () => {
	const { t } = useTranslation();
	const {
		tipo,
		comune,
		sort,
		comuniOptions,
		tipologieOptions,
		setAllParams,
		reset,
		nFiltri,
	} = useRicerca();

	return (
		<>
			<SelectFilter
				defaultValue={""}
				handleReset={async () => {
					await setAllParams({
						tipo: "",
					});
				}}
				id="tipologia-filter"
				label={t.annunci.fs_tipologia.titolo}
				onValueChange={async (value: string) => {
					await setAllParams({
						tipo: value,
					});
				}}
				options={tipologieOptions}
				placeholder={t.seleziona_placeholder}
				value={tipo}
			/>
			<SelectFilter
				defaultValue={undefined}
				handleReset={async () => {
					await setAllParams({
						comune: null,
					});
				}}
				id="comune-filter"
				key={comune}
				label={t.annunci.comune}
				onValueChange={async (value: string) => {
					await setAllParams({
						comune: value,
					});
				}}
				options={comuniOptions}
				placeholder={t.seleziona_placeholder}
				value={comune || undefined}
			/>
			<ConsegnaFilter />
			<BudgetFilters />
			<SelectFilter
				defaultValue={sort}
				handleReset={async () => {
					await setAllParams({
						sort: "Recenti",
					});
				}}
				id="sort-filter"
				label={t.ordina_per}
				onValueChange={async (value: SortTypes) => {
					await setAllParams({
						sort: value,
					});
				}}
				options={sort_options}
				placeholder={t.seleziona_placeholder}
				value={sort}
			/>
			<Button
				className="flex flex-row gap-2"
				disabled={nFiltri === 0}
				onClick={reset}
				variant="destructive"
			>
				<FilterX /> {t.annunci.filtri_reset}
			</Button>
		</>
	);
};

const RicercaSideFilters = () => {
	const isBigScreen = useMediaQuery("(min-width: 96rem)");

	if (!isBigScreen) return null;
	return (
		<div className="flex h-fit w-full max-w-72 shrink-0 grow-0 flex-col gap-3 overflow-clip rounded-md bg-secondary p-2 outline-neutral-300">
			<Filters />
		</div>
	);
};

const DrowdownFilters = () => {
	const { t } = useTranslation();
	const { nFiltri } = useRicerca();
	const isBigScreen = useMediaQuery("(min-width: 96rem)");

	if (isBigScreen) return null;
	return (
		<div className="flex w-full sm:w-auto 2xl:hidden">
			<DropdownMenu>
				<DropdownMenuTrigger asChild>
					<Button
						className="group flex w-full flex-row gap-2 sm:w-auto"
						variant="default"
					>
						<Badge className="w-fit rounded-md bg-secondary px-1.5 py-0.5 text-secondary-foreground dark:group-hover:bg-transparent">
							{nFiltri}
						</Badge>
						<Filter />{" "}
						{nFiltri === 1 ? t.annunci.filtro_attivo : t.annunci.filtri_attivi}
						<ChevronDown />
					</Button>
				</DropdownMenuTrigger>
				<DropdownMenuContent
					align="start"
					className="flex w-full min-w-80 max-w-3xl flex-col gap-2 p-4"
				>
					<Filters />
				</DropdownMenuContent>
			</DropdownMenu>
		</div>
	);
};

const RicercaHeader = () => {
	const { map, setAllParams } = useRicerca();

	return (
		<div className="mx-auto w-full max-w-500 space-y-5">
			<div className="flex w-full flex-col-reverse gap-2 sm:flex-row">
				<div className="flex w-full items-center gap-2 sm:w-auto">
					<DrowdownFilters />
					<Button
						className="flex w-fit flex-row items-center gap-2 md:w-24"
						onClick={async () => {
							await setAllParams({
								map: !map,
							});
						}}
						variant="default"
					>
						{!map ? (
							<>
								<MapIcon /> <span className="hidden sm:block">Mappa</span>
							</>
						) : (
							<>
								<Table />
								<span className="hidden sm:block">Lista</span>
							</>
						)}
					</Button>
				</div>
				<div className="flex w-full items-center justify-between gap-2 sm:justify-between">
					<div className="relative z-35 flex w-full gap-1 sm:w-auto">
						<CodiceBox
							className="h-9 w-full"
							inputId="annunci-search"
							optionBoxClassName="top-11 "
						/>
					</div>

					<CTA_TipologiaModal />
				</div>
			</div>
		</div>
	);
};

const AnnunciList = () => {
	const {
		tipo,
		comune,
		consegna,
		sort,
		map,
		minBudget,
		maxBudget,
		isLoading: isUpdatingRicerca,
	} = useRicerca();

	const { data, status, isLoading } = api.annunci.annunciRicerca.useQuery(
		{
			comune: comune || undefined,
			consegna: consegna || undefined,
			sort: sort || undefined,
			tipologia: tipo || undefined,
			minBudget: minBudget || undefined,
			maxBudget: maxBudget || undefined,
		},
		{
			enabled: !map && !isUpdatingRicerca,
			placeholderData: keepPreviousData,
			staleTime: 5000,
		},
	);

	if (isLoading) return <LoadingPage />;
	if (status === "error") return <Status500 />;

	return (
		<div className="w-full space-y-4">
			{status === "pending" ? (
				<LoadingPage />
			) : (
				<div className="relative z-0 mx-auto grid w-full grid-flow-row grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4">
					{data.map((annuncio, idx) => (
						<LazyCardAnnuncio
							key={annuncio.codice}
							{...annuncio}
							eager={idx <= 9}
							videos={[]}
						/>
					))}
				</div>
			)}
		</div>
	);
};

interface SelectFilterProps<T> {
	label: string;
	id: string;
	options: FilterOption<string>[];
	defaultValue?: T;
	value: T | undefined;
	onValueChange: (value: T) => void;
	handleReset: () => void;
	placeholder: string;
}

export const SelectFilter = <T extends string>({
	label,
	id,
	options,
	defaultValue,
	value,
	onValueChange,
	placeholder,
	handleReset,
}: SelectFilterProps<T>) => {
	return (
		<div className="flex flex-col gap-2">
			<div className="flex h-4 flex-row gap-4 px-1">
				<Label
					className={cn(
						"font-semibold text-muted-foreground",
						value !== defaultValue && "text-primary",
					)}
					htmlFor={id}
				>
					{label}
				</Label>
				{value !== defaultValue && (
					<button
						className="cursor-pointer"
						onClick={handleReset}
						type="button"
					>
						<RotateCcwIcon className="size-4" />
					</button>
				)}
			</div>

			<Select
				defaultValue={defaultValue}
				onValueChange={onValueChange}
				value={value}
			>
				<SelectTrigger
					className="w-full bg-primary-foreground text-primary"
					id={id}
				>
					<SelectValue placeholder={placeholder} />
				</SelectTrigger>
				<SelectContent>
					{options.map((option) => (
						<SelectItem
							className="text-left"
							disabled={option.disabled}
							key={option.value}
							value={option.value}
						>
							{option.value}
						</SelectItem>
					))}
				</SelectContent>
			</Select>
		</div>
	);
};

const ConsegnaFilter = () => {
	const { t } = useTranslation();
	const { consegna: consegna2, setAllParams, consegnaOptions } = useRicerca();

	const mappedConsegnaOptions = useMemo(() => {
		if (consegnaOptions.length === 0) return [];
		return consegnaOptions
			.map((c) =>
				t.parametri.mesi[c.value]
					? {
							value: c.value,
							label: t.parametri.mesi[c.value],
							disabled: c.disabled,
						}
					: null,
			)
			.filter((v) => v !== null);
	}, [consegnaOptions, t.parametri.mesi]);

	const onValueChange = async (value: number) => {
		if (consegna2?.includes(value)) {
			const newConsegna2 = consegna2.filter((c) => c !== value);
			await setAllParams({
				consegna: newConsegna2.length > 0 ? newConsegna2 : null,
			});
			return;
		}

		const newConsegna2 = consegna2 ? [...consegna2, value] : [value];
		await setAllParams({
			consegna: newConsegna2,
		});
		return;
	};
	return (
		<div className="flex flex-col gap-2">
			<div className="flex h-4 flex-row gap-4 px-1">
				<Label
					className={cn(
						"font-semibold text-muted-foreground",
						consegna2 !== null && consegna2.length > 0 && "text-primary",
					)}
				>
					{t.annunci.consegna}
				</Label>
				{consegna2 !== null && consegna2.length > 0 && (
					<button
						className="cursor-pointer"
						onClick={async () => {
							await setAllParams({
								consegna: null,
							});
						}}
						type="button"
					>
						<RotateCcwIcon className="size-4" />
					</button>
				)}
			</div>

			<div className="flex flex-wrap gap-1 rounded-md bg-background p-1">
				{mappedConsegnaOptions.map(({ value, label, disabled }) => (
					<Button
						className={cn(
							consegna2?.includes(value) && "border border-primary",
						)}
						disabled={disabled && !consegna2?.includes(value)}
						key={value}
						onClick={async () => {
							await onValueChange(value);
						}}
						size="sm"
						variant={consegna2?.includes(value) ? "default" : "outline"}
					>
						<span className="text-sm">{label?.substring(0, 3)}</span>
					</Button>
				))}
			</div>
		</div>
	);
};
const MAX_SLIDER_VALUE = 1500;
const BudgetFilters = () => {
	const { t } = useTranslation();
	const { minBudget, maxBudget, setAllParams } = useRicerca();

	const minDebounced = useCallback(
		debounce(async (value: number | null) => {
			await setAllParams({
				minBudget: value,
			});
		}, 500),
		[setAllParams],
	);

	const maxDebounced = useCallback(
		debounce(async (value: number | null) => {
			await setAllParams({
				maxBudget: value,
			});
		}, 500),
		[setAllParams],
	);

	const [sliderValue, setSliderValue] = useState<[number, number]>([
		Number(minBudget) / 100 || 0,
		Number(maxBudget) / 100 || MAX_SLIDER_VALUE,
	]);

	const onSliderChangeEnd = () => {
		const [min, max] = sliderValue;
		minDebounced(min === 0 ? null : min * 100);
		maxDebounced(max === MAX_SLIDER_VALUE ? null : max * 100);
	};
	useEffect(() => {
		setSliderValue([
			Number(minBudget) / 100 || 0,
			Number(maxBudget) / 100 || MAX_SLIDER_VALUE,
		]);
	}, [minBudget, maxBudget]);

	return (
		<>
			<div className="flex w-full flex-col gap-2">
				<div className="flex flex-row items-center gap-4">
					<Label
						className={cn("font-semibold text-muted-foreground")}
						htmlFor="slider"
					>
						{t.annunci.budget}
					</Label>
					{(minBudget !== null || maxBudget !== null) && (
						<button
							className="cursor-pointer"
							onClick={async () => {
								await setAllParams({
									minBudget: null,
									maxBudget: null,
								});
							}}
							type="button"
						>
							<RotateCcwIcon className="size-4" />
						</button>
					)}
				</div>
				<Slider
					className="mt-2"
					id="slider"
					max={MAX_SLIDER_VALUE}
					min={0}
					onValueChange={(value) => setSliderValue(value as [number, number])}
					onValueCommit={onSliderChangeEnd}
					step={100}
					value={sliderValue}
				/>
				<div className="flex items-center justify-between text-muted-foreground text-sm">
					<span>{sliderValue[0]} €</span>
					<span>
						{sliderValue[1] === MAX_SLIDER_VALUE ? "∞" : sliderValue[1]} €
					</span>
				</div>
			</div>
		</>
	);
};
