import { goto, replaceState } from "$app/navigation";
import { page } from "$app/state";
import { passengerInfoVM } from "$lib/domains/passengerinfo/view/passenger.info.vm.svelte";
import {
	type FlightPriceDetails,
	type FlightTicket,
	ticketSearchPayloadModel,
} from "$lib/domains/ticket/data/entities/index";
import { trpcApiStore } from "$lib/stores/api";
import type { Result } from "@pkg/result";
import { nanoid } from "nanoid";
import { toast } from "svelte-sonner";
import { get } from "svelte/store";
import { flightTicketStore, ticketSearchStore } from "../data/store";
import { checkoutVM } from "./checkout/checkout.vm.svelte";
import { paymentInfoVM } from "./checkout/payment-info-section/payment.info.vm.svelte";
import {
	MaxStops,
	SortOption,
	ticketFiltersStore,
} from "./ticket-filters.vm.svelte";

export class FlightTicketViewModel {
	searching = $state(false);
	tickets = $state<FlightTicket[]>([]);
	renderedTickets = $state<FlightTicket[]>([]);

	updatingPrices = $state(false);

	beginSearch() {
		const info = get(ticketSearchStore);
		if (!info) {
			return;
		}
		if (
			info.passengerCounts.adults < 1 &&
			info.passengerCounts.children < 1
		) {
			toast.error("Please enter at least one adult and one child");
			return;
		}

		const sum = info.passengerCounts.adults + info.passengerCounts.children;
		if (sum > 10) {
			toast.error("Please enter no more than 10 passengers");
			return;
		}

		const params = this.formatURLParams();
		goto(`/search?${params.toString()}`);
	}

	loadStore(urlParams: URLSearchParams) {
		console.log("Meta parameter: ", urlParams.get("meta"));

		ticketSearchStore.update((prev) => {
			return {
				sessionId: prev.sessionId ?? "",
				ticketType: urlParams.get("ticketType") ?? prev.ticketType,
				cabinClass: urlParams.get("cabinClass") ?? prev.cabinClass,
				passengerCounts: {
					adults: Number(
						urlParams.get("adults") ?? prev.passengerCounts.adults,
					),
					children: Number(
						urlParams.get("children") ??
							prev.passengerCounts.children,
					),
				},
				departure: urlParams.get("departure") ?? prev.departure,
				arrival: urlParams.get("arrival") ?? prev.arrival,
				departureDate:
					urlParams.get("departureDate") ?? prev.departureDate,
				returnDate: urlParams.get("returnDate") ?? prev.returnDate,
				loadMore: prev.loadMore ?? false,
				meta: (() => {
					const metaStr = urlParams.get("meta");
					if (!metaStr) return prev.meta;
					try {
						return JSON.parse(metaStr);
					} catch (e) {
						console.error("Failed to parse meta parameter:", e);
						return prev.meta;
					}
				})(),
			};
		});
	}

	resetCachedCheckoutData() {
		// @ts-ignore
		flightTicketStore.set(undefined);
		passengerInfoVM.reset();
		checkoutVM.reset();
		paymentInfoVM.reset();
	}

	async searchForTickets(loadMore = false) {
		const api = get(trpcApiStore);
		if (!api) {
			return toast.error("Please try again by reloading the page", {
				description: "Page state not properly initialized",
			});
		}
		let payload = get(ticketSearchStore);
		if (!payload) {
			return toast.error(
				"Could not search for tickets due to invalid payload",
			);
		}

		const parsed = ticketSearchPayloadModel.safeParse(payload);
		if (!parsed.success) {
			console.log("Enter some parameters to search for tickets");
			this.searching = false;
			return;
		}
		payload = parsed.data;

		if (loadMore) {
			payload.loadMore = true;
		}

		this.searching = true;
		const out = await api.ticket.searchTickets.query(payload);
		this.searching = false;

		console.log(out);

		if (out.error) {
			return toast.error(out.error.message, {
				description: out.error.userHint,
			});
		}
		if (!out.data) {
			this.tickets = [];
			return toast.error("No search results", {
				description: "Please try again with different parameters",
			});
		}

		this.tickets = out.data;
		this.applyFilters();
		this.resetCachedCheckoutData();
	}

	applyFilters() {
		this.searching = true;
		const filters = get(ticketFiltersStore);
		const filteredTickets = this.tickets.filter((ticket) => {
			// Price filter
			if (filters.priceRange.max > 0) {
				if (
					ticket.priceDetails.displayPrice < filters.priceRange.min ||
					ticket.priceDetails.displayPrice > filters.priceRange.max
				) {
					return false;
				}
			}

			if (filters.maxStops !== MaxStops.Any) {
				// Calculate stops for outbound flight
				const outboundStops =
					ticket.flightIteneraries.outbound.length - 1;
				// Calculate stops for inbound flight
				const inboundStops = ticket.flightIteneraries.inbound.length - 1;

				// Get the maximum number of stops between outbound and inbound
				const maxStopsInJourney = Math.max(outboundStops, inboundStops);

				switch (filters.maxStops) {
					case MaxStops.Direct:
						if (maxStopsInJourney > 0) return false;
						break;
					case MaxStops.One:
						if (maxStopsInJourney > 1) return false;
						break;
					case MaxStops.Two:
						if (maxStopsInJourney > 2) return false;
						break;
				}
			}

			// Time range filters
			if (filters.time.departure.max > 0 || filters.time.arrival.max > 0) {
				const allItineraries = [
					...ticket.flightIteneraries.outbound,
					...ticket.flightIteneraries.inbound,
				];
				for (const itinerary of allItineraries) {
					const departureHour = new Date(
						itinerary.departure.utcTime,
					).getHours();
					const arrivalHour = new Date(
						itinerary.destination.utcTime,
					).getHours();

					if (filters.time.departure.max > 0) {
						if (
							departureHour < filters.time.departure.min ||
							departureHour > filters.time.departure.max
						) {
							return false;
						}
					}

					if (filters.time.arrival.max > 0) {
						if (
							arrivalHour < filters.time.arrival.min ||
							arrivalHour > filters.time.arrival.max
						) {
							return false;
						}
					}
				}
			}

			// Duration filter
			if (filters.duration.max > 0) {
				const allItineraries = [
					...ticket.flightIteneraries.outbound,
					...ticket.flightIteneraries.inbound,
				];
				const totalDuration = allItineraries.reduce(
					(sum, itinerary) => sum + itinerary.durationSeconds,
					0,
				);
				const durationHours = totalDuration / 3600;

				if (
					durationHours < filters.duration.min ||
					durationHours > filters.duration.max
				) {
					return false;
				}
			}

			// Overnight filter
			if (!filters.allowOvernight) {
				const allItineraries = [
					...ticket.flightIteneraries.outbound,
					...ticket.flightIteneraries.inbound,
				];
				const hasOvernightFlight = allItineraries.some((itinerary) => {
					const departureHour = new Date(
						itinerary.departure.utcTime,
					).getHours();
					const arrivalHour = new Date(
						itinerary.destination.utcTime,
					).getHours();

					// Consider a flight overnight if it departs between 8 PM (20) and 6 AM (6)
					return (
						departureHour >= 20 ||
						departureHour <= 6 ||
						arrivalHour >= 20 ||
						arrivalHour <= 6
					);
				});
				if (hasOvernightFlight) return false;
			}

			return true;
		});

		filteredTickets.sort((a, b) => {
			switch (filters.sortBy) {
				case SortOption.PriceLowToHigh:
					return (
						a.priceDetails.displayPrice - b.priceDetails.displayPrice
					);
				case SortOption.PriceHighToLow:
					return (
						b.priceDetails.displayPrice - a.priceDetails.displayPrice
					);
				default:
					return 0;
			}
		});

		this.renderedTickets = filteredTickets;
		this.searching = false;
	}

	async cacheTicketAndGotoCheckout(id: number) {
		const api = get(trpcApiStore);
		if (!api) {
			return toast.error("Please try again by reloading the page", {
				description: "Page state not properly initialized",
			});
		}
		const targetTicket = this.tickets.find((ticket) => ticket.id === id);
		if (!targetTicket) {
			return toast.error("Ticket not found", {
				description:
					"Please try again with different parameters or refresh page to try again",
			});
		}
		const sid = get(ticketSearchStore).sessionId;
		console.log("sid", sid);
		const out = await api.ticket.cacheTicket.mutate({
			sid,
			payload: targetTicket,
		});
		if (out.error) {
			return toast.error(out.error.message, {
				description: out.error.userHint,
			});
		}
		if (!out.data) {
			return toast.error("Failed to proceed to checkout", {
				description: "Please refresh the page to try again",
			});
		}
		goto(`/checkout/${sid}/${out.data}`);
	}

	redirectToSearchPage() {
		const params = this.formatURLParams();
		goto(`/search?${params.toString()}`);
	}

	setURLParams(): Result<boolean> {
		ticketSearchStore.update((prev) => {
			return { ...prev, sessionId: nanoid() };
		});
		const newParams = this.formatURLParams();

		const url = new URL(page.url.href);

		for (const [key, value] of newParams.entries()) {
			url.searchParams.set(key, value);
		}

		let stripped = page.url.href.includes("?")
			? page.url.href.split("?")[0]
			: page.url.href;

		replaceState(
			new URL(stripped + "?" + new URLSearchParams(newParams)).toString(),
			{},
		);

		return { data: true };
	}

	private formatURLParams() {
		const info = get(ticketSearchStore);
		let out = new URLSearchParams();
		if (!info) {
			return out;
		}
		out.append("ticketType", info.ticketType);
		out.append("cabinClass", info.cabinClass);
		out.append("adults", info.passengerCounts.adults.toString());
		out.append("children", info.passengerCounts.children.toString());
		out.append("departureDate", info.departureDate);
		out.append("returnDate", info.returnDate);
		out.append("meta", JSON.stringify(info.meta));
		return out;
	}

	async updateTicketPrices(updated: FlightPriceDetails) {
		const api = get(trpcApiStore);
		if (!api) {
			return;
		}
		const tid = get(flightTicketStore).id;
		this.updatingPrices = true;
		const out = await api.ticket.updateTicketPrices.mutate({
			tid,
			payload: updated,
		});
		this.updatingPrices = false;
		console.log("new shit");
		console.log(out);
		if (out.error) {
			toast.error(out.error.message, {
				description: out.error.userHint,
			});
		}
		if (!out.data) {
			return;
		}
		flightTicketStore.update((prev) => {
			return { ...prev, priceDetails: out.data! };
		});
	}
}

export const flightTicketVM = new FlightTicketViewModel();
