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

export class OrderRepository {
	private db: Database;

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

	async listActiveOrders(): Promise<Result<LimitedOrderWithProductModel[]>> {
		const conditions = [
			or(
				eq(order.status, OrderStatus.PENDING_FULFILLMENT),
				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,
				orderPrice: true,
				fullfilledPrice: true,
				status: true,
			},
			with: {
				product: true,
				customerInfo: {
					columns: {
						firstName: true,
						middleName: true,
						lastName: true,
						email: true,
						country: true,
						state: true,
					},
				},
			},
		});

		const out = [] as LimitedOrderWithProductModel[];

		for (const order of qRes) {
			const parsed = limitedOrderWithProductModel.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 getOrderByUID(uid: string): Promise<Result<FullOrderModel>> {
		const out = await this.db.query.order.findFirst({
			where: eq(order.uid, uid),
			with: { customerInfo: true, product: true },
		});
		if (!out) return {};
		const parsed = fullOrderModel.safeParse({ ...out });
		if (!parsed.success) {
			return {
				error: getError(
					{
						code: ERROR_CODES.INTERNAL_SERVER_ERROR,
						message: "An error occured while finding order",
						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; uid: string }>> {
		const uid = nanoid(12).toUpperCase();
		try {
			const out = await this.db
				.insert(order)
				.values({
					uid,
					displayPrice: payload.displayPrice.toFixed(3),
					basePrice: payload.basePrice.toFixed(3),
					discountAmount: payload.discountAmount.toFixed(3),
					fullfilledPrice: payload.fullfilledPrice.toFixed(3),
					orderPrice: payload.orderPrice.toFixed(3),

					paymentInfoId: payload.paymentInfoId,

					status: OrderStatus.PENDING_FULFILLMENT,

					customerInfoId: payload.customerInfoId,
					productId: payload.productId,

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

			return { data: { id: out[0]?.id, uid } };
		} 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 };
	}
}
