import { ERROR_CODES, type Result } from "$lib/core/data.types";
import { and, eq, isNotNull, or, type Database } from "@pkg/db";
import { order, passengerInfo } from "@pkg/db/schema";
import { getError, Logger } from "@pkg/logger";
import { nanoid } from "nanoid";
import {
	fullOrderModel,
	limitedOrderWithTicketInfoModel,
	OrderStatus,
	type FullOrderModel,
	type LimitedOrderWithTicketInfoModel,
	type NewOrderModel,
} from "./entities";

export class OrderRepository {
	private db: Database;

	constructor(db: Database) {
		this.db = db;
	}

	async listActiveOrders(): Promise<Result<LimitedOrderWithTicketInfoModel[]>> {
		const conditions = [
			or(
				eq(order.status, OrderStatus.PENDING_FULLFILLMENT),
				eq(order.status, OrderStatus.PARTIALLY_FULFILLED),
			),
			isNotNull(order.agentId),
		];
		const qRes = await this.db.query.order.findMany({
			where: and(...conditions),
			columns: {
				id: true,
				displayPrice: true,
				basePrice: true,
				discountAmount: true,
				pricePerPassenger: true,
				fullfilledPrice: true,
				status: true,
			},
			with: {
				flightTicketInfo: {
					columns: {
						id: true,
						departure: true,
						arrival: true,
						departureDate: true,
						returnDate: true,
						flightType: true,
						passengerCounts: true,
					},
				},
			},
		});

		const out = [] as LimitedOrderWithTicketInfoModel[];

		for (const order of qRes) {
			const parsed = limitedOrderWithTicketInfoModel.safeParse({
				...order,
			});
			if (!parsed.success) {
				return {
					error: getError(
						{
							code: ERROR_CODES.INTERNAL_SERVER_ERROR,
							message: "An error occured while parsing order",
							userHint: "Please try again later",
							actionable: false,
							detail: "An error occured while parsing order",
						},
						parsed.error,
					),
				};
			}
			out.push(parsed.data);
		}
		return { data: out };
	}

	async getOrderByPNR(pnr: string): Promise<Result<FullOrderModel>> {
		const out = await this.db.query.order.findFirst({
			where: eq(order.pnr, pnr),
			with: { flightTicketInfo: true },
		});
		if (!out) return {};
		const relatedPassengerInfos = await this.db.query.passengerInfo.findMany({
			where: eq(passengerInfo.orderId, out.id),
			with: { passengerPii: true },
		});
		const parsed = fullOrderModel.safeParse({
			...out,
			emailAccount: undefined,
			passengerInfos: relatedPassengerInfos,
		});
		if (!parsed.success) {
			return {
				error: getError(
					{
						code: ERROR_CODES.INTERNAL_SERVER_ERROR,
						message: "An error occured while finding booking",
						userHint: "Please try again later",
						detail: "An error occured while parsing order",
					},
					parsed.error,
				),
			};
		}
		return { data: parsed.data };
	}

	async createOrder(
		payload: NewOrderModel,
	): Promise<Result<{ id: number; pnr: string }>> {
		const pnr = nanoid(9).toUpperCase();
		try {
			const out = await this.db
				.insert(order)
				.values({
					displayPrice: payload.displayPrice.toFixed(3),
					basePrice: payload.basePrice.toFixed(3),
					discountAmount: payload.discountAmount.toFixed(3),

					flightTicketInfoId: payload.flightTicketInfoId,
					paymentInfoId: payload.paymentInfoId,

					status: OrderStatus.PENDING_FULLFILLMENT,
					pnr,

					createdAt: new Date(),
					updatedAt: new Date(),
				})
				.returning({ id: order.id })
				.execute();

			return { data: { id: out[0]?.id, pnr } };
		} catch (e) {
			return {
				error: getError(
					{
						code: ERROR_CODES.DATABASE_ERROR,
						message: "An error occured while creating order",
						userHint: "Please try again later",
						actionable: false,
						detail: "An error occured while creating order",
					},
					e,
				),
			};
		}
	}

	async markAgentsOrderAsFulfilled(oid: number): Promise<Result<boolean>> {
		try {
			const out = await this.db
				.update(order)
				.set({ status: OrderStatus.FULFILLED })
				.where(and(eq(order.id, oid), isNotNull(order.agentId)))
				.returning({ id: order.id })
				.execute();
			return { data: out && out.length > 0 };
		} catch (e) {
			return {
				error: getError(
					{
						code: ERROR_CODES.DATABASE_ERROR,
						message:
							"An error occured while marking order as fulfilled",
						userHint: "Please try again later",
						actionable: false,
						detail: "An error occured while marking order as fulfilled",
					},
					e,
				),
			};
		}
	}

	async deleteOrder(id: number) {
		Logger.info(`Deleting order with id ${id}`);
		const out = await this.db.delete(order).where(eq(order.id, id)).execute();
		Logger.debug(out);
		return { data: true };
	}
}
