import { SessionOutcome } from "$lib/domains/ckflow/data/entities";
import { getCKUseCases } from "$lib/domains/ckflow/domain/usecases";
import { getCustomerInfoController } from "$lib/domains/customerinfo/controller";
import {
	createOrderPayloadModel,
	OrderCreationStep,
} from "$lib/domains/order/data/entities";
import { PaymentInfoRepository } from "$lib/domains/paymentinfo/data/repository";
import { PaymentInfoUseCases } from "$lib/domains/paymentinfo/domain/usecases";
import { getProductUseCases } from "$lib/domains/product/usecases";
import { createTRPCRouter, publicProcedure } from "$lib/trpc/t";
import { db } from "@pkg/db";
import { getError, Logger } from "@pkg/logger";
import { ERROR_CODES, type Result } from "@pkg/result";
import { z } from "zod";
import { OrderRepository } from "../data/repository";
import { OrderController } from "./controller";

export const orderRouter = createTRPCRouter({
	/**
	 * Creates a new order for product checkout
	 * Handles customer info creation, payment info creation, and order creation
	 */
	createOrder: publicProcedure
		.input(createOrderPayloadModel)
		.mutation(async ({ input }) => {
			const piuc = new PaymentInfoUseCases(new PaymentInfoRepository(db));
			const orderController = new OrderController(new OrderRepository(db));
			const customerInfoController = getCustomerInfoController();
			const productUC = getProductUseCases();

			// Validate required inputs
			if (!input.productId || !input.customerInfo) {
				return {
					error: getError({
						code: ERROR_CODES.INPUT_ERROR,
						message: "Missing required order information",
						detail: "Product ID and customer information are required",
						userHint:
							"Please ensure product and customer information are provided",
					}),
				} as Result<string>;
			}

			// Verify product exists
			Logger.info(`Verifying product with ID: ${input.productId}`);
			const productRes = await productUC.getProductById(input.productId);
			if (productRes.error || !productRes.data) {
				return {
					error: getError({
						code: ERROR_CODES.NOT_FOUND_ERROR,
						message: "Product not found",
						detail: `Product with ID ${input.productId} does not exist`,
						userHint: "Please select a valid product",
					}),
				} as Result<string>;
			}

			// Create customer information
			Logger.info("Creating customer information");
			const customerRes = await customerInfoController.createCustomerInfo(
				input.customerInfo,
			);
			if (customerRes.error || !customerRes.data) {
				return { error: customerRes.error } as Result<string>;
			}
			const customerInfoId = customerRes.data;
			Logger.info(`Customer info created with ID: ${customerInfoId}`);

			// Create payment information if provided
			let paymentInfoId: number | undefined = undefined;
			if (input.paymentInfo) {
				Logger.info("Creating payment information");
				const paymentRes = await piuc.createPaymentInfo(
					input.paymentInfo,
				);
				if (paymentRes.error || !paymentRes.data) {
					// Cleanup customer info on payment failure
					await customerInfoController.deleteCustomerInfo(
						customerInfoId,
					);
					return { error: paymentRes.error } as Result<string>;
				}
				paymentInfoId = paymentRes.data;
				Logger.info(`Payment info created with ID: ${paymentInfoId}`);
			}

			// Set IDs in order model
			input.orderModel.productId = input.productId;
			input.orderModel.customerInfoId = customerInfoId;
			input.orderModel.paymentInfoId = paymentInfoId;

			// Create the order
			Logger.info("Creating order");
			const orderRes = await orderController.createOrder(input.orderModel);
			if (orderRes.error || !orderRes.data) {
				// Cleanup on order creation failure
				if (paymentInfoId) {
					await piuc.deletePaymentInfo(paymentInfoId);
				}
				await customerInfoController.deleteCustomerInfo(customerInfoId);
				return { error: orderRes.error } as Result<string>;
			}

			const orderId = orderRes.data.id;
			const orderUID = orderRes.data.uid;
			Logger.info(`Order created successfully with ID: ${orderId}`);

			if (paymentInfoId) {
				await piuc.updatePaymentInfoOrderId(paymentInfoId, orderId);
			}

			// Update checkout flow state if flowId is provided
			//
			if (input.flowId) {
				Logger.info(
					`Updating checkout flow state for flow ${input.flowId}`,
				);
				try {
					await getCKUseCases().cleanupFlow(input.flowId, {
						sessionOutcome: SessionOutcome.COMPLETED,
						checkoutStep: OrderCreationStep.SUMMARY,
					});
					Logger.info(
						`Checkout flow ${input.flowId} marked as completed`,
					);
				} catch (err) {
					Logger.warn(
						`Failed to update checkout flow state: ${input.flowId}`,
					);
					Logger.error(err);
				}
			}

			Logger.debug(
				"Rotating the link id for the product now that it's been used",
			);

			const out = await productUC.refreshProductLinkId(input.productId);

			Logger.debug(
				`Product link id rotated: ${JSON.stringify(out, null, 2)}`,
			);

			Logger.info("Order creation completed successfully");
			return { data: orderUID } as Result<string>;
		}),

	/**
	 * Finds an order by its ID
	 */
	findByIdUID: publicProcedure
		.input(z.object({ uid: z.string() }))
		.query(async ({ input }) => {
			const orderController = new OrderController(new OrderRepository(db));
			return orderController.getOrderByUID(input.uid);
		}),
});
