import { TRPCError } from "@trpc/server";
import { jsonArrayFrom } from "kysely/helpers/postgres";
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
import type {
	NewRinnovi,
	RinnoviId,
	RinnoviUpdate,
} from "~/schemas/public/Rinnovi";
import type { UsersId } from "~/schemas/public/Users";
import { RinnovoSolver } from "~/server/controllers/pagamenti.controller";
import { db } from "~/server/db";
import {
	createOrdine,
	type NewRinnovoOrdini,
} from "~/server/services/ordini.service";

export const getRinnoviUser = async (userId: UsersId) => {
	try {
		return await db
			.selectFrom("rinnovi")
			.where("userId", "=", userId)
			.selectAll()
			.execute();
	} catch (error) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Could not fetch rinnovi for user: ${(error as Error).message}`,
		});
	}
};

export type RinnovoData = Awaited<ReturnType<typeof getRinnovoData>>;

export const getRinnovoData = async (rinnovoId: RinnoviId) => {
	try {
		return await db
			.selectFrom("rinnovi")
			.where("id", "=", rinnovoId)
			.selectAll("rinnovi")
			.select((eb) =>
				jsonArrayFrom(
					eb
						.selectFrom("ordini")
						.whereRef("ordini.rinnovo_id", "=", "rinnovi.id")
						.selectAll("ordini"),
				).as("ordini"),
			)
			.executeTakeFirstOrThrow(
				() => new Error(`Rinnovo not found - id: ${rinnovoId}`),
			);
	} catch (error) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: `Error getting Rinnovo by id: ${(error as Error).message}`,
		});
	}
};

export const createRinnovo = async (data: NewRinnovi) => {
	try {
		const rinnovo = await db
			.insertInto("rinnovi")
			.values(data)
			.returningAll()
			.executeTakeFirstOrThrow(() => new Error("Failed to create rinnovo"));

		const pack = await RinnovoSolver(rinnovo.decorrenza, rinnovo.scadenza);
		if (!pack.success) {
			throw new Error("No suitable pack found for rinnovo");
		}

		const ordineData: NewRinnovoOrdini = {
			rinnovo_id: rinnovo.id,
			userid: rinnovo.userId,
			type: OrderTypeEnum.Rinnovo,
			amount_cent: pack.pack.prezzo_cent,
			packid: pack.pack.idprezziario,
			isActive: false,
		};
		await createOrdine(ordineData);

		return rinnovo;
	} catch (error) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Could not create rinnovo: ${(error as Error).message}`,
		});
	}
};

export const updateRinnovo = async ({
	rinnovoId,
	data,
}: {
	rinnovoId: RinnoviId;
	data: RinnoviUpdate;
}) => {
	try {
		if (data.decorrenza && data.scadenza) {
			const rinnovo = await db
				.selectFrom("rinnovi")
				.where("id", "=", rinnovoId)
				.selectAll()
				.executeTakeFirstOrThrow(
					() => new Error(`Rinnovo not found - id: ${rinnovoId}`),
				);

			if (
				new Date(data.decorrenza).getTime() !==
					new Date(rinnovo.decorrenza).getTime() ||
				new Date(data.scadenza).getTime() !==
					new Date(rinnovo.scadenza).getTime()
			) {
				//no need to update ordine if permanenza is not being updated

				const existingPack = await RinnovoSolver(
					rinnovo.decorrenza,
					rinnovo.scadenza,
				);
				if (!existingPack.success) {
					throw new Error("No suitable pack found for rinnovo");
				}
				const newPack = await RinnovoSolver(
					new Date(data.decorrenza),
					new Date(data.scadenza),
				);
				if (!newPack.success) {
					throw new Error("No suitable pack found for rinnovo");
				}
				if (existingPack.pack.idprezziario !== newPack.pack.idprezziario) {
					//update ordine with new pack and price if permanenza is being updated and ordine is not active yet
					await db
						.updateTable("ordini")
						.set({
							amount_cent: newPack.pack.prezzo_cent,
							packid: newPack.pack.idprezziario,
						})
						.where("rinnovo_id", "=", rinnovoId)
						.where("isActive", "=", false)
						.execute();
				}
			}
		}

		return await db
			.updateTable("rinnovi")
			.set(data)
			.where("id", "=", rinnovoId)
			.returningAll()
			.executeTakeFirstOrThrow(
				() => new Error(`Failed to update rinnovo - id: ${rinnovoId}`),
			);
	} catch (error) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error updating rinnovo: ${(error as Error).message}`,
		});
	}
};

export const deleteRinnovo = async (rinnovoId: RinnoviId) => {
	try {
		await db.deleteFrom("rinnovi").where("id", "=", rinnovoId).execute();
		return true;
	} catch (error) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error deleting rinnovo: ${(error as Error).message}`,
		});
	}
};
