import { convertAndFormatCurrency } from "$lib/domains/currency/view/currency.vm.svelte";
import { db } from "@pkg/db";
import { getError, Logger } from "@pkg/logger";
import { DiscountType, type CouponModel } from "@pkg/logic/domains/coupon/data";
import { ERROR_CODES, type Result } from "@pkg/result";
import type {
	FlightPriceDetails,
	FlightTicket,
	TicketSearchPayload,
} from "../data/entities";
import { TicketRepository } from "../data/repository";
import { ScrapedTicketsDataSource } from "../data/scrape.data.source";

export class TicketController {
	private repo: TicketRepository;
	private TICKET_SCRAPERS = ["kiwi"];

	constructor(repo: TicketRepository) {
		this.repo = repo;
	}

	async searchForTickets(
		payload: TicketSearchPayload,
		coupon: CouponModel | undefined,
	): Promise<Result<FlightTicket[]>> {
		const result = await this.repo.searchForTickets({
			sessionId: payload.sessionId,
			ticketSearchPayload: payload,
			providers: this.TICKET_SCRAPERS,
		});

		if (result.error || !result.data) {
			return result;
		}

		if (!coupon) {
			return result;
		}

		Logger.info(`Auto-applying coupon ${coupon.code} to all search results`);

		return {
			data: result.data.map((ticket) => {
				return this.applyDiscountToTicket(ticket, coupon);
			}),
		};
	}

	private applyDiscountToTicket(
		ticket: FlightTicket,
		coupon: CouponModel,
	): FlightTicket {
		// Calculate discount amount
		let discountAmount = 0;

		if (coupon.discountType === DiscountType.PERCENTAGE) {
			discountAmount =
				(ticket.priceDetails.displayPrice *
					Number(coupon.discountValue)) /
				100;
		} else {
			discountAmount = Number(coupon.discountValue);
		}

		// Apply maximum discount limit if specified
		if (
			coupon.maxDiscountAmount &&
			discountAmount > Number(coupon.maxDiscountAmount)
		) {
			discountAmount = Number(coupon.maxDiscountAmount);
		}

		// Skip if minimum order value is not met
		if (
			coupon.minOrderValue &&
			ticket.priceDetails.displayPrice < Number(coupon.minOrderValue)
		) {
			return ticket;
		}

		// Create a copy of the ticket with the discount applied
		const discountedTicket = { ...ticket };

		discountedTicket.priceDetails = {
			...ticket.priceDetails,
			discountAmount:
				(ticket.priceDetails.discountAmount || 0) + discountAmount,
			displayPrice: ticket.priceDetails.displayPrice - discountAmount,
			appliedCoupon: coupon.code,
			couponDescription:
				coupon.description ||
				`Coupon discount of ${coupon.discountType === DiscountType.PERCENTAGE ? coupon.discountValue + "%" : convertAndFormatCurrency(parseFloat(coupon.discountValue.toString()))}`,
		};

		return discountedTicket;
	}

	async cacheTicket(sid: string, payload: FlightTicket) {
		Logger.info(
			`Caching ticket for ${sid} | ${payload.departure}:${payload.arrival}`,
		);
		const refOIds = payload.refOIds;
		if (!refOIds) {
			// In this case we're not going for any of our fancy checkout jazz
			return this.repo.createTicket(payload, true);
		}

		const areAnyLive =
			await this.repo.areCheckoutAlreadyLiveForOrder(refOIds);

		Logger.info(`Any of the OIds has checkout session live ?? ${areAnyLive}`);

		if (areAnyLive) {
			return {
				error: getError({
					code: ERROR_CODES.INTERNAL_SERVER_ERROR,
					message: "This ticket offer has expired",
					userHint:
						"Please select another one or perform search again to fetch latest offers",
					actionable: false,
					detail: "Failed to ticket",
				}),
			};
		}
		Logger.info("nu'uh seems greenlight to make a ticket");
		return this.repo.createTicket(payload, true);
	}

	async updateTicketPrices(tid: number, payload: FlightPriceDetails) {
		return this.repo.updateTicketPrices(tid, payload);
	}

	async uncacheAndSaveTicket(tid: number) {
		return this.repo.uncacheAndSaveTicket(tid);
	}

	async getCheckoutUrlByRefOId(id: number) {
		return this.repo.getCheckoutUrlByRefOId(id);
	}

	async setRefOIdsForTicket(tid: number, oids: number[]) {
		return this.repo.setRefOIdsForTicket(tid, oids);
	}

	async getTicketById(id: number) {
		return this.repo.getTicketById(id);
	}
}

export function getTC() {
	const ds = new ScrapedTicketsDataSource("", "");
	return new TicketController(new TicketRepository(db, ds));
}
