import { TRPCError } from "@trpc/server";
import { add } from "date-fns";
import { sql } from "kysely";
import { jsonArrayFrom, jsonObjectFrom } from "kysely/helpers/postgres";
import { z } from "zod/v4";
import type { PurchaseData } from "~/components/acquisto_receipt";
import { env } from "~/env";
import type { AnnunciId } from "~/schemas/public/Annunci";
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
import type { OrdiniOrdineId } from "~/schemas/public/Ordini";
import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum";
import type {
	Payments,
	PaymentsId,
	PaymentsUpdate,
} from "~/schemas/public/Payments";
import type {
	Prezziario,
	PrezziarioIdprezziario,
} from "~/schemas/public/Prezziario";
import type {
	NewServizio,
	ServizioServizioId,
	ServizioUpdate,
} from "~/schemas/public/Servizio";
import type { ServizioAnnunciUpdate } from "~/schemas/public/ServizioAnnunci";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import type { Users, UsersId } from "~/schemas/public/Users";
import type { UsersAnagraficaUpdate } from "~/schemas/public/UsersAnagrafica";
import type { Context } from "~/server/api/trpc";
import {
	addEventToQueue,
	canSendNotification,
	nextTimeForNotification,
} from "~/server/controllers/event_queue.controller";
import { cleanupUnusedOrders } from "~/server/controllers/ordini.controller";
import {
	type MinimalSMSData,
	SendMessage,
} from "~/server/controllers/skebby.controller";
import { db } from "~/server/db";
import { NewMail, type NewMailProps } from "~/server/services/mailer";
import { getUser } from "~/server/services/user.service";
import { withImages, withVideos } from "~/utils/kysely-helper";
import { GetUserInterests } from "../services/interests.service";
import { getPrezziarioByIdHandler } from "../services/prezziario.service";
import type { AnnuncioRicerca } from "./annunci.controller";

export const addServizio = async (data: NewServizio) => {
	try {
		return await db
			.insertInto("servizio")
			.values(data)
			.returningAll()
			.executeTakeFirstOrThrow();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error adding new servizio: ${(e as Error).message}`,
		});
	}
};

export const getServizioById = async (servizioId: ServizioServizioId) => {
	try {
		return await db
			.selectFrom("servizio")
			.selectAll()
			.where("servizio_id", "=", servizioId)
			.executeTakeFirstOrThrow();
	} catch (e) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: `Servizio not found: ${(e as Error).message}`,
		});
	}
};

export const updateServizio = async ({
	servizioId,
	data,
}: {
	servizioId: ServizioServizioId;
	data: ServizioUpdate;
}) => {
	try {
		return await db
			.updateTable("servizio")
			.set(data)
			.where("servizio_id", "=", servizioId)
			.returningAll()
			.executeTakeFirstOrThrow();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error updating servizio: ${(e as Error).message}`,
		});
	}
};

export const deleteServizio = async (servizioId: ServizioServizioId) => {
	try {
		await db
			.deleteFrom("servizio")
			.where("servizio_id", "=", servizioId)
			.executeTakeFirstOrThrow();
		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error deleting servizio: ${(e as Error).message}`,
		});
	}
};

export const getServiziByUserId = async (userId: UsersId) => {
	try {
		return await db
			.selectFrom("servizio")
			.selectAll("servizio")
			.select((eb) => [
				jsonArrayFrom(
					eb
						.selectFrom("servizio_annunci")
						.select(["servizio_annunci.annunci_id"])
						.innerJoin("annunci", "annunci.id", "servizio_annunci.annunci_id")
						.select("annunci.codice")
						.whereRef(
							"servizio_annunci.servizio_id",
							"=",
							"servizio.servizio_id",
						),
				).as("annunci"),
			])
			.where("user_id", "=", userId)
			.orderBy("created_at", "asc")
			.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching servizi by user ID: ${(e as Error).message}`,
		});
	}
};
export const getPreOnboardData = async (servizioId: ServizioServizioId) => {
	try {
		const servizio = await getServizioById(servizioId);

		if (!servizio) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Servizio not found",
			});
		}

		const userData = await db
			.selectFrom("users")
			.select([
				"users.id",
				"users.nome",
				"users.cognome",
				"users.email",
				"users.telefono",
			])
			.where("id", "=", servizio.user_id)
			.executeTakeFirstOrThrow();

		if (!userData) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "User not found",
			});
		}
		const hasPrevious = await db
			.selectFrom("servizio")
			.select("servizio_id")
			.where("user_id", "=", servizio.user_id)
			.where("servizio_id", "!=", servizioId)
			.where("isOkAcconto", "=", true)
			.executeTakeFirst();

		return { hasPrevious: !!hasPrevious, servizio, userData };
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching pre-onboard data: ${(e as Error).message}`,
		});
	}
};
export const getDataPerAcquisto = async (servizioId: ServizioServizioId) => {
	try {
		const servizio = await getServizioById(servizioId);

		if (!servizio) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Servizio not found",
			});
		}

		const userData = await db
			.selectFrom("users")
			.select([
				"users.id",
				"users.nome",
				"users.cognome",
				"users.email",
				"users.telefono",
			])
			.where("id", "=", servizio.user_id)
			.executeTakeFirstOrThrow();

		if (!userData) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "User not found",
			});
		}
		const anagrafica = await db
			.selectFrom("users_anagrafica")
			.selectAll("users_anagrafica")
			.where("users_anagrafica.userid", "=", servizio.user_id)
			.executeTakeFirst();
		return {
			anagrafica,
			servizio,
			userData,
		};
	} catch (e) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: `Servizio not found: ${(e as Error).message}`,
		});
	}
};

export const postAcquistoDataHandler = async ({
	servizioId,
	userId,
	user,
	anagrafica,
	servizio,
}: {
	servizioId: ServizioServizioId;
	userId: UsersId;
	user: Pick<Users, "nome" | "cognome" | "email" | "telefono" | "username">;
	anagrafica: UsersAnagraficaUpdate;
	servizio: ServizioUpdate;
}): Promise<{ ordineIdForPayment: OrdiniOrdineId | null }> => {
	try {
		console.log("servizio", servizio);
		const servizioData = await db.transaction().execute(async (tx) => {
			// Update user and anagrafica data
			await tx
				.updateTable("users")
				.set(user)
				.where("id", "=", userId)
				.executeTakeFirstOrThrow();

			const { idanagrafica, ...rest } = anagrafica;
			if (idanagrafica) {
				await tx
					.updateTable("users_anagrafica")
					.set({
						...rest,
						userid: userId,
					})
					.where("idanagrafica", "=", idanagrafica)
					.returningAll()
					.executeTakeFirstOrThrow();
			} else {
				await tx
					.insertInto("users_anagrafica")
					.values({
						...rest,
						userid: userId,
					})
					.returningAll()
					.executeTakeFirstOrThrow();
			}
			// Update servizio data
			return await tx
				.updateTable("servizio")
				.set(servizio)
				.where("servizio_id", "=", servizioId)
				.returningAll()
				.executeTakeFirstOrThrow();
		});

		if (servizioData.skipPayment) {
			// If skipping order, set servizio as active with decorrenza today
			await updateServizio({
				data: {
					decorrenza: new Date(),
					isOkAcconto: true,
					isOkSaldo: true,
					isOkConsulenza: true,
				},
				servizioId,
			});
			return { ordineIdForPayment: null };
		}
		//cleanup unused payments

		await db
			.deleteFrom("payments")
			.where("servizio_id", "=", servizioId)
			.where("paymentstatus", "is", null)
			.execute();

		if (
			!servizioData ||
			servizioData.isInterrotto ||
			servizioData.isOkAcconto ||
			servizioData.decorrenza !== null
		) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Cannot create payment for this servizioData",
			});
		}
		const packId =
			servizioData.tipologia === TipologiaPosizioneEnum.Transitorio
				? ("ACCONTO_BD" as PrezziarioIdprezziario)
				: ("ACCONTO_CA" as PrezziarioIdprezziario);

		const acconto = await db
			.selectFrom("prezziario")
			.select("idprezziario")
			.where("isActive", "=", true)
			.where("idprezziario", "=", packId)
			.executeTakeFirst();

		if (!acconto) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Acconto not found",
			});
		}

		const existingOrder = await db
			.selectFrom("ordini")
			.select("ordini.ordine_id")
			.where("ordini.servizio_id", "=", servizioData.servizio_id)
			.where("ordini.packid", "=", acconto.idprezziario)
			.where("ordini.type", "=", OrderTypeEnum.Acconto)
			.where("ordini.isActive", "=", false)
			.orderBy("ordini.created_at", "desc")
			.executeTakeFirst();

		if (existingOrder) {
			await db
				.deleteFrom("ordini")
				.where("ordini.isActive", "=", false)
				.where("ordine_id", "!=", existingOrder.ordine_id)
				.execute();
			return { ordineIdForPayment: existingOrder.ordine_id };
		}

		const order = await db
			.insertInto("ordini")
			.values({
				packid: acconto.idprezziario,
				servizio_id: servizioData.servizio_id,
				type: OrderTypeEnum.Acconto,
				userid: userId,
			})
			.returning("ordine_id")
			.executeTakeFirstOrThrow();

		return { ordineIdForPayment: order.ordine_id };
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error updating data for acquisto: ${(e as Error).message}`,
		});
	}
};

const SaldoTransitorioMapping: Record<number, PrezziarioIdprezziario> = {
	0: "SALDOBD_1MESE" as PrezziarioIdprezziario,
	1: "SALDOBD_3MESI" as PrezziarioIdprezziario,
	2: "SALDOBD_6MESI" as PrezziarioIdprezziario,
	3: "SALDOBD_9MESI" as PrezziarioIdprezziario,
	4: "SALDOBD_12MESI" as PrezziarioIdprezziario,
};

export const setupSaldoServizio = async ({
	servizioId,
	annuncioId,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
}) => {
	try {
		return await db.transaction().execute(async (tx) => {
			// Cleanup previous payments and orders
			await cleanupUnusedOrders({ db: tx });

			const servizio = await tx
				.selectFrom("servizio")
				.selectAll()
				.where("servizio_id", "=", servizioId)
				.executeTakeFirstOrThrow();

			if (
				!servizio.isOkAcconto ||
				servizio.isInterrotto ||
				servizio.decorrenza == null ||
				servizio.isOkSaldo
			) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Servizio not found",
				});
			}

			const annuncio = await tx
				.selectFrom("servizio_annunci")
				.selectAll()
				.where("servizio_id", "=", servizioId)
				.where("annunci_id", "=", annuncioId)
				.executeTakeFirstOrThrow();
			if (
				annuncio.user_confirmed_at == null ||
				annuncio.accettato_conferma_at == null
			) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Annuncio not found",
				});
			}
			let saldo:
				| Pick<
						Prezziario,
						"idprezziario" | "prezzo_cent" | "nome_it" | "nome_en" | "sconto"
				  >
				| undefined;

			const qry = tx
				.selectFrom("prezziario")
				.select(["idprezziario", "prezzo_cent", "nome_it", "nome_en", "sconto"])
				.where("isActive", "=", true);

			if (servizio.tipologia === TipologiaPosizioneEnum.Transitorio) {
				const mpp =
					servizio.permanenza && SaldoTransitorioMapping[servizio.permanenza];
				if (!servizio.permanenza || !mpp) {
					throw new TRPCError({
						code: "NOT_FOUND",
						message: "Permanenza not set for transitorio servizio",
					});
				}
				saldo = await qry.where("idprezziario", "=", mpp).executeTakeFirst();
			} else if (servizio.tipologia === TipologiaPosizioneEnum.Stabile) {
				if (servizio.budget >= 500) {
					saldo = await qry
						.where("idprezziario", "=", "SALDOCA500+" as PrezziarioIdprezziario)

						.executeTakeFirst();
				} else {
					saldo = await qry
						.where("idprezziario", "=", "SALDOCA500" as PrezziarioIdprezziario)
						.executeTakeFirst();
				}
			}

			if (!saldo) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Acconto not found",
				});
			}
			await tx
				.deleteFrom("payments")
				.where("servizio_id", "=", servizioId)
				.where("paymentstatus", "is", null)
				.execute();

			const order = await db
				.insertInto("ordini")
				.values({
					packid: saldo.idprezziario,
					servizio_id: servizio.servizio_id,
					type: OrderTypeEnum.Saldo,
					userid: servizio.user_id,
				})
				.returning("ordine_id")
				.executeTakeFirstOrThrow();

			return order.ordine_id;
		});
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error setting up saldo servizio: ${(e as Error).message}`,
		});
	}
};

const ConsulenzaPriceMapping = {
	"4 + 4": "CONS_4+4" as PrezziarioIdprezziario,
	"30 Giorni": "CONS_30GG" as PrezziarioIdprezziario,
	Agevolato: "CONS_3+2" as PrezziarioIdprezziario,
	Commerciale: "CONS_COMM" as PrezziarioIdprezziario,
	Foresteria: "CONS_COMM" as PrezziarioIdprezziario,
	"Transi.Age": "CONS_3+2" as PrezziarioIdprezziario,
	Transitorio: "CONS_TRAN" as PrezziarioIdprezziario,
};

type GetPackError = { success: false; message: string };
type GetPackResult = { success: true; pack: Prezziario };

type ServizioPacks = {
	acconto: GetPackResult | GetPackError;
	saldo: GetPackResult | GetPackError;
};
export const getPacksPerServizio = async (
	servizioId: ServizioServizioId,
): Promise<ServizioPacks> => {
	const servizio = await getServizioById(servizioId);
	if (!servizio) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: "Servizio non trovato",
		});
	}

	const getAcconto = async (): Promise<GetPackResult | GetPackError> => {
		switch (servizio.tipologia) {
			case TipologiaPosizioneEnum.Transitorio: {
				const pack = await getPrezziarioByIdHandler({
					id: "ACCONTO_BD" as PrezziarioIdprezziario,
				});

				if (!pack) {
					return { success: false, message: "Pack non trovato" };
				}
				return {
					success: true,
					pack,
				};
			}

			case TipologiaPosizioneEnum.Stabile: {
				const pack = await getPrezziarioByIdHandler({
					id: "ACCONTO_CA" as PrezziarioIdprezziario,
				});

				if (!pack) {
					return { success: false, message: "Pack non trovato" };
				}
				return {
					success: true,
					pack,
				};
			}
			default:
				return { success: false, message: "Seleziona tipologia" };
		}
	};

	const getSaldo = async (): Promise<GetPackResult | GetPackError> => {
		switch (servizio.tipologia) {
			case TipologiaPosizioneEnum.Transitorio: {
				const mpp =
					servizio.permanenza && SaldoTransitorioMapping[servizio.permanenza];
				if (!servizio.permanenza || !mpp) {
					return {
						success: false,
						message: "Permanenza non selezionata",
					};
				}
				const pack = await getPrezziarioByIdHandler({ id: mpp });
				if (!pack) {
					return { success: false, message: "Pack non trovato" };
				}

				return { success: true, pack };
			}
			case TipologiaPosizioneEnum.Stabile: {
				const pack = await getPrezziarioByIdHandler({
					id: (servizio.budget >= 500
						? "SALDOCA500+"
						: "SALDOCA500") as PrezziarioIdprezziario,
				});
				if (!pack) {
					return { success: false, message: "Pack non trovato" };
				}
				return {
					success: true,
					pack,
				};
			}
			default:
				return { success: false, message: "Seleziona tipologia" };
		}
	};

	return {
		acconto: await getAcconto(),
		saldo: await getSaldo(),
	};
};
type TipoEnum = keyof typeof ConsulenzaPriceMapping;
export const setupSaldoConsulenzaServizio = async ({
	servizioId,
	annuncioId,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
}) => {
	try {
		return await db.transaction().execute(async (tx) => {
			// Cleanup previous payments and orders
			await cleanupUnusedOrders({ db: tx });

			const servizio = await tx
				.selectFrom("servizio")
				.selectAll()
				.where("servizio_id", "=", servizioId)
				.executeTakeFirstOrThrow();

			if (
				!servizio.isOkAcconto ||
				servizio.isInterrotto ||
				servizio.decorrenza == null ||
				!servizio.isOkSaldo ||
				servizio.isOkConsulenza
			) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Non serve saldo per questo servizio",
				});
			}

			const annuncio = await tx
				.selectFrom("servizio_annunci")
				.selectAll()
				.where("servizio_id", "=", servizioId)
				.where("annunci_id", "=", annuncioId)
				.executeTakeFirstOrThrow();
			if (
				annuncio.user_confirmed_at == null ||
				annuncio.accettato_conferma_at == null ||
				!annuncio.contratto_tipo
			) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Annuncio not found or contratto not set",
				});
			}

			if (
				!Object.keys(ConsulenzaPriceMapping).includes(annuncio.contratto_tipo)
			) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Consulenza type not found",
				});
			}

			const pack = await tx
				.selectFrom("prezziario")
				.select(["idprezziario", "prezzo_cent", "nome_it", "nome_en", "sconto"])
				.where("isActive", "=", true)
				.where(
					"idprezziario",
					"=",
					ConsulenzaPriceMapping[annuncio.contratto_tipo as TipoEnum],
				)
				.executeTakeFirst();

			if (!pack) {
				throw new TRPCError({
					code: "NOT_FOUND",
					message: "Pack not found",
				});
			}
			await tx
				.deleteFrom("payments")
				.where("servizio_id", "=", servizioId)
				.where("paymentstatus", "is", null)
				.execute();

			const order = await db
				.insertInto("ordini")
				.values({
					packid: pack.idprezziario,
					servizio_id: servizio.servizio_id,
					type: OrderTypeEnum.Consulenza,
					userid: servizio.user_id,
				})
				.returning("ordine_id")
				.executeTakeFirstOrThrow();

			return order.ordine_id;
		});
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error setting up consulenza servizio: ${(e as Error).message}`,
		});
	}
};

export type ServizioData = Awaited<
	ReturnType<typeof getAllServizioAnnunci>
>[number];
export const getAllServizioAnnunci = async (userId: UsersId) => {
	try {
		return await db
			.selectFrom("servizio")
			.where("user_id", "=", userId)
			.leftJoin(
				"users_anagrafica",
				"servizio.user_id",
				"users_anagrafica.userid",
			)
			.select((eb) => [
				"servizio.servizio_id",
				"servizio.created_at",
				"servizio.decorrenza",
				"servizio.tipologia",
				"servizio.budget",
				"servizio.permanenza",
				"servizio.isInterrotto",
				"servizio.isOkAcconto",
				"servizio.isOkSaldo",
				"servizio.isOkConsulenza",
				"servizio.skipPayment",
				"servizio.skipControlloDoc",
				"servizio.skipDocMotivazione",
				"servizio.n_adulti",
				"servizio.n_minori",
				"servizio.entromese",
				jsonObjectFrom(
					eb
						.selectFrom("users_storage")
						.select([
							"users_storage.storageId as storageId",
							"users_anagrafica.doc_personale_fronte_ref as ref",
						])
						.whereRef(
							"users_storage.user_storage_id",
							"=",
							"users_anagrafica.doc_personale_fronte_ref",
						),
				).as("doc_personale_fronte"),
				jsonObjectFrom(
					eb
						.selectFrom("users_storage")
						.select([
							"users_storage.storageId as storageId",
							"users_anagrafica.doc_personale_retro_ref as ref",
						])
						.whereRef(
							"users_storage.user_storage_id",
							"=",
							"users_anagrafica.doc_personale_retro_ref",
						),
				).as("doc_personale_retro"),
				jsonObjectFrom(
					eb
						.selectFrom("users_storage")
						.select([
							"users_storage.storageId as storageId",
							"servizio.doc_motivazione_ref as ref",
						])
						.whereRef(
							"users_storage.user_storage_id",
							"=",
							"servizio.doc_motivazione_ref",
						),
				).as("doc_motivazione"),
				jsonArrayFrom(
					eb
						.selectFrom("servizio_annunci")
						.selectAll("servizio_annunci")
						.innerJoin("annunci", "annunci.id", "servizio_annunci.annunci_id")
						.select((_eb) => [
							"annunci.id",
							"annunci.codice",
							"annunci.comune",
							"annunci.provincia",
							"annunci.prezzo",
							"annunci.consegna",
							"annunci.numero_camere",
							"annunci.mq",
							"annunci.tipo",
							"annunci.titolo_it",
							"annunci.titolo_en",
							"annunci.desc_en",
							"annunci.desc_it",
							"annunci.modificato_il",
							"annunci.stato",
							"annunci.external_videos",
							"annunci.media_updated_at",
							"annunci.homepage",
							"annunci.web",
							"annunci.disponibile_da",
							"annunci.persone",
							"annunci.permanenza",
							withImages(),
							withVideos(),
						])
						//ignora annunci che non sono disponibili
						.where("annunci.web", "=", true)
						.where("annunci.stato", "!=", "Sospeso")
						.orderBy("servizio_annunci.user_confirmed_at", (ob) =>
							ob.asc().nullsLast(),
						)
						.orderBy("servizio_annunci.open_contatti_at", (ob) =>
							ob.asc().nullsLast(),
						)
						.orderBy("servizio_annunci.created_at", "asc")
						.whereRef(
							"servizio_annunci.servizio_id",
							"=",
							"servizio.servizio_id",
						),
				).as("annunci"),
			])

			.orderBy("servizio.created_at", "desc")
			.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching servizio annunci: ${(e as Error).message}`,
		});
	}
};

export const addServizioAnnunci = async ({
	servizioId,
	annunci,
}: {
	servizioId: ServizioServizioId;
	annunci: AnnunciId[];
}) => {
	try {
		await db
			.insertInto("servizio_annunci")
			.values(
				annunci.map((annuncio, idx) => ({
					annunci_id: annuncio,
					created_at: add(new Date(), {
						//trick per assicurare che la data sia sempre diversa
						seconds: idx,
					}),
					servizio_id: servizioId,
				})),
			)
			.onConflict((oc) => oc.columns(["servizio_id", "annunci_id"]).doNothing())
			.execute();
		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error adding servizio to annuncio: ${(e as Error).message}`,
		});
	}
};

export const getServizioAnnuncio = async ({
	servizioId,
	annuncioId,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
}) => {
	try {
		return await db
			.selectFrom("servizio_annunci")
			.selectAll()
			.where("servizio_id", "=", servizioId)
			.where("annunci_id", "=", annuncioId)
			.executeTakeFirstOrThrow();
	} catch (e) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: `Servizio not found: ${(e as Error).message}`,
		});
	}
};

export const updateServizioAnnuncio = async ({
	servizioId,
	annuncioId,
	data,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
	data: ServizioAnnunciUpdate;
}) => {
	try {
		await db
			.updateTable("servizio_annunci")
			.set(data)
			.where("servizio_id", "=", servizioId)
			.where("annunci_id", "=", annuncioId)
			.execute();

		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error updating servizio from annuncio: ${(e as Error).message}`,
		});
	}
};

export const getAnnunciAvailableToAdd = async (
	servizioId: ServizioServizioId,
) => {
	try {
		return await db.transaction().execute(async (tx) => {
			const servizioAnnunci = await tx
				.selectFrom("servizio_annunci")
				.select("annunci_id")
				.where("servizio_id", "=", servizioId)
				.execute();

			const ids = servizioAnnunci.map((item) => item.annunci_id);
			let qry = tx
				.selectFrom("annunci")
				.select(["id", "codice"])
				.where("annunci.web", "=", true)
				.where("stato", "!=", "Sospeso");

			if (ids.length > 0) {
				qry = qry.where("id", "not in", ids);
			}
			return await qry.execute();
		});
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching available annunci: ${(e as Error).message}`,
		});
	}
};

export const deleteServizioAnnuncio = async (
	servizioId: ServizioServizioId,
	annuncioId: AnnunciId,
) => {
	try {
		await db
			.deleteFrom("servizio_annunci")
			.where("servizio_id", "=", servizioId)
			.where("annunci_id", "=", annuncioId)
			.execute();
		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error deleting servizio from annuncio: ${(e as Error).message}`,
		});
	}
};

export const SbloccaContatti = async ({
	servizioId,
	annuncioId,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
}) => {
	try {
		const alreadyUnlocked = await db
			.selectFrom("servizio_annunci")
			.where("servizio_id", "=", servizioId)
			.where("open_contatti_at", "is not", null)
			.select((eb) => [eb.fn.count<number>("annunci_id").as("count")])
			.executeTakeFirst();
		if (alreadyUnlocked && alreadyUnlocked.count >= 10) {
			return false;
		}

		const data = await db.transaction().execute(async (tx) => {
			await tx
				.updateTable("servizio_annunci")
				.set({
					open_contatti_at: new Date(),
				})
				.where("servizio_id", "=", servizioId)
				.where("annunci_id", "=", annuncioId)
				.execute();

			const servizio = await tx
				.selectFrom("servizio")
				.select("user_id")
				.where("servizio_id", "=", servizioId)
				.executeTakeFirstOrThrow(() => new Error("Servizio not found"));

			const user = await tx
				.selectFrom("users")
				.select(["id", "username", "email", "telefono"])
				.where("id", "=", servizio.user_id)
				.executeTakeFirstOrThrow(() => new Error("User not found"));

			const anagrafica = await tx
				.selectFrom("users_anagrafica")
				.select("sesso")
				.where("userid", "=", servizio.user_id)
				.executeTakeFirstOrThrow(() => new Error("Anagrafica not found"));

			const annuncio = await tx
				.selectFrom("annunci")
				.where("id", "=", annuncioId)
				.select([
					"codice",
					"indirizzo",
					"email",
					"numero",
					"locatore",
					"civico",
					"comune",
				])
				.executeTakeFirstOrThrow(() => new Error("Annuncio not found"));

			return {
				anagrafica,
				annuncio,
				annuncioId,
				servizioId,
				user,
			};
		});

		const EmailProprietario: NewMailProps | null = data.annuncio.email
			? {
					template: {
						mailType: "emailContattoInteressato",
						props: {
							indirizzo: data.annuncio.indirizzo,
							nome_cognome: data.annuncio.locatore || "",
							nome_cognome_utente: data.user.username,
							sesso: data.anagrafica.sesso,
							telefono: data.user.telefono,
						},
					},
					mail: {
						subject: "Un interessato ha ricevuto i Suoi contatti",
						to: data.annuncio.email,
					},
				}
			: null;

		const EmailInteressato: NewMailProps = {
			template: {
				mailType: "generic",
				props: {
					link: {
						href: `${env.BASE_URL}/area-riservata/dashboard#${servizioId}-${annuncioId}`,
						label: "Vai al servizio",
					},
					noreply: true,
					testo: `I contatti per l'annuncio ${data.annuncio.codice} sono stati sbloccati con successo.`,
					title: "Contatti sbloccati per il servizio di ricerca affitto",
				},
			},
			mail: {
				subject: "Contatti sbloccati per il servizio di ricerca affitto",
				to: data.user.email,
			},
			userId: data.user.id,
		};
		const numeroProprietario = data.annuncio.numero
			? data.annuncio.numero.replace(/\s+/g, "").split(",")[0] || null
			: null;

		const SmsProprietario: MinimalSMSData | null = numeroProprietario
			? {
					message: `${data.anagrafica.sesso ? (data.anagrafica.sesso === "M" ? "Il Sig." : "La Sig.ra") : "Il/La Sig./Sig.ra"} ${data.user.username}, con numero ${data.user.telefono} ha sbloccato i contatti per l'annuncio in ${data.annuncio.indirizzo} ${data.annuncio.civico} a ${data.annuncio.comune}.`,
					recipient: [numeroProprietario],
				}
			: null;

		await NewMail(EmailInteressato);

		if (canSendNotification()) {
			if (EmailProprietario) {
				await NewMail(EmailProprietario);
			}
			if (SmsProprietario) {
				await SendMessage(SmsProprietario);
			}
		} else {
			const lock_expires = nextTimeForNotification();
			if (EmailProprietario) {
				await addEventToQueue({
					data: {
						data: EmailProprietario,
						tipo: "email",
					},
					lock_expires,
				});
			}
			if (SmsProprietario) {
				await addEventToQueue({
					data: {
						data: SmsProprietario,
						tipo: "sms",
					},
					lock_expires,
				});
			}
		}
		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error unlocking contatti: ${(e as Error).message}`,
		});
	}
};

export const interruzioneServizio = async (servizioId: ServizioServizioId) => {
	try {
		const servizio = await getServizioById(servizioId);

		if (!servizio) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Servizio not found",
			});
		}

		if (servizio.isInterrotto) {
			throw new TRPCError({
				code: "BAD_REQUEST",
				message: "Servizio already interrupted",
			});
		}
		if (servizio.decorrenza == null) {
			throw new TRPCError({
				code: "BAD_REQUEST",
				message: "Servizio decorrenza not set",
			});
		}
		const isWithinRipensamento =
			new Date() <=
			add(servizio.decorrenza, {
				days: 14,
			});

		if (!isWithinRipensamento) {
			throw new TRPCError({
				code: "BAD_REQUEST",
				message: "Periodo di ripensamento scaduto",
			});
		}

		await db
			.updateTable("servizio")
			.set({
				isInterrotto: true,
			})
			.where("servizio_id", "=", servizioId)
			.executeTakeFirstOrThrow();

		// NOTIFICHE EMAIL INTERRUZIONE SERVIZIO
		const utente = await getUser({ db, userId: servizio.user_id });
		await NewMail({
			template: {
				mailType: "generic",
				props: {
					noreply: true,
					testo: `Abbiamo ricevuto la tua richiesta di interruzione del servizio di ricerca affitto per la tipologia ${servizio.tipologia}. Il servizio è stato interrotto con successo.`,
					title: `Servizio Ricerca Affitto ${servizio.tipologia} interrotto`,
				},
			},
			mail: {
				subject: `Servizio Ricerca Affitto ${servizio.tipologia} interrotto`,
				to: utente.email,
			},
			userId: utente.id,
		});

		await NewMail({
			template: {
				mailType: "generic",
				props: {
					link: {
						href: `${env.BASE_URL}/area-riservata/admin/user-view/ricerca/${servizio.user_id}#${servizioId}`,
						label: "Vai al servizio",
					},
					noreply: true,
					testo: `L'utente ha richiesto l'interruzione del servizio di ricerca affitto per la tipologia ${servizio.tipologia}. Puoi procedere con le prossime azioni.`,
					title: `Utente ${utente.username} ha richieto l'interruzione del servizio di ricerca affitto`,
				},
			},
			mail: {
				subject: `Utente ${utente.username} ha richieto l'interruzione del servizio di ricerca affitto`,
				to: "web@infoalloggi.it",
			},
		});

		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error interruzione servizio: ${(e as Error).message}`,
		});
	}
};

export const InteractionLogicTipologia = async ({
	tipologia,
	userId,
	annuncioId,
}: {
	tipologia: string;
	userId: UsersId;
	annuncioId: AnnunciId;
}) => {
	const parse = z.custom<TipologiaPosizioneEnum>();
	const parsedTipologia = parse.safeParse(tipologia);
	if (!parsedTipologia.success) {
		return { status: "invalid" as const };
	}
	const servizi = await db
		.selectFrom("servizio")
		.select(["tipologia", "servizio_id"])
		.where("tipologia", "=", parsedTipologia.data)
		.where("isOkAcconto", "=", true)
		.where("isInterrotto", "=", false)
		.where("decorrenza", "is not", null)
		.where("user_id", "=", userId)
		.select((eb) => [
			jsonObjectFrom(
				eb
					.selectFrom("servizio_annunci")
					.select("annunci_id")
					.where("servizio_annunci.annunci_id", "=", annuncioId)
					.whereRef("servizio.servizio_id", "=", "servizio_annunci.servizio_id")
					.limit(1),
			).as("alreadySaved"),
		])

		.executeTakeFirst();

	if (!servizi) {
		return { status: "not_found" as const };
	}

	if (servizi.alreadySaved) {
		return {
			servizioId: servizi.servizio_id,
			status: "already_saved" as const,
		};
	}
	return {
		servizioId: servizi.servizio_id,
		status: "found" as const,
		tipologia: servizi.tipologia,
	};
};

export const userSendConfermaIntent = async ({
	servizioId,
	annuncioId,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
}) => {
	try {
		const servizio = await getServizioById(servizioId);
		if (
			!servizio ||
			servizio.isInterrotto ||
			!servizio.isOkAcconto ||
			servizio.decorrenza == null
		) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Servizio not found",
			});
		}
		await db
			.updateTable("servizio_annunci")
			.set({
				user_confirmed_at: new Date(),
			})
			.where("servizio_id", "=", servizioId)
			.where("annunci_id", "=", annuncioId)
			.executeTakeFirstOrThrow();

		// NOTIFICHE EMAIL CONFERMA IMMOBILE

		const utente = await getUser({ db, userId: servizio.user_id });

		const annuncio_codice = await db
			.selectFrom("annunci")
			.select("codice")
			.where("id", "=", annuncioId)
			.executeTakeFirstOrThrow();

		await NewMail({
			template: {
				mailType: "generic",
				props: {
					link: {
						href: `${env.BASE_URL}/area-riservata/admin/user-view/ricerca/${servizio.user_id}#${servizioId}-${annuncioId}`,
						label: "Visualizza Annuncio",
					},
					noreply: true,
					testo: `L'utente ${utente.username} ha confermato l'immobile con codice ${annuncio_codice.codice}.`,
					title: `Nuova conferma immobile ${annuncio_codice.codice}`,
				},
			},
			mail: {
				subject: `Nuova conferma immobile ${annuncio_codice.codice}`,
				to: "web@infoalloggi.it",
			},
		});

		await NewMail({
			template: {
				mailType: "generic",
				props: {
					noreply: true,
					testo: `La tua conferma per l'immobile con codice ${annuncio_codice.codice} è stata inviata con successo. Il nostro team la esaminerà a breve. Riceverai una notifica via email per procedere.`,
					title: `Conferma immobile ${annuncio_codice.codice} inviata con successo`,
				},
			},
			mail: {
				subject: `Conferma immobile ${annuncio_codice.codice} inviata con successo`,
				to: utente.email,
			},
			userId: utente.id,
		});
		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error confirming immobile: ${(e as Error).message}`,
		});
	}
};

export const adminUpdateConfermaStatus = async ({
	servizioId,
	annuncioId,
	status,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
	status: boolean;
}) => {
	try {
		await db
			.updateTable("servizio_annunci")
			.set({
				hasConfermaAdmin: status,
			})
			.where("servizio_id", "=", servizioId)
			.where("annunci_id", "=", annuncioId)
			.execute();

		const servizio = await getServizioById(servizioId);
		const utente = await getUser({ db, userId: servizio.user_id });
		//ADMIN CONFERMA LAVORATA EMAIL
		if (status) {
			await NewMail({
				template: {
					mailType: "generic",
					props: {
						link: {
							href: `${env.BASE_URL}/area-riservata/dashboard#${servizioId}-${annuncioId}`,
							label: "Vai al servizio",
						},
						noreply: true,
						testo: `La conferma è stata lavorata con successo. Puoi procedere con le prossime azioni.`,
						title: "Conferma lavorata per il servizio di ricerca affitto",
					},
				},
				mail: {
					subject: "Conferma lavorata per il servizio di ricerca affitto",
					to: utente.email,
				},
				userId: utente.id,
			});
		}

		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error updating conferma status from annuncio: ${(e as Error).message}`,
		});
	}
};

export const userAccettaConferma = async ({
	servizioId,
	annuncioId,
}: {
	servizioId: ServizioServizioId;
	annuncioId: AnnunciId;
}) => {
	try {
		await db
			.updateTable("servizio_annunci")
			.set({
				accettato_conferma_at: new Date(),
			})
			.where("servizio_id", "=", servizioId)
			.where("annunci_id", "=", annuncioId)
			.execute();

		const servizio = await getServizioById(servizioId);
		const utente = await getUser({ db, userId: servizio.user_id });

		// USER ACCETTA CONFERMA EMAIL

		await NewMail({
			template: {
				mailType: "generic",
				props: {
					link: {
						href: `${env.BASE_URL}/area-riservata/admin/user-view/ricerca/${servizio.user_id}#${servizioId}-${annuncioId}`,
						label: "Vai al servizio",
					},
					noreply: true,
					testo: `L'utente ha accettato le condizioni di conferma per la locazione. Puoi procedere con le prossime azioni.`,
					title: `Utente ${utente.username} ha accettato le condizioni di conferma`,
				},
			},
			mail: {
				subject: `Utente ${utente.username} ha accettato le condizioni di conferma`,
				to: "web@infoalloggi.it",
			},
		});

		return { success: true, userId: utente.id };
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error accepting conferma from annuncio: ${(e as Error).message}`,
		});
	}
};
export const isOrdineAwaitingPayment = async (ordineId: OrdiniOrdineId) => {
	try {
		const ordine = await db
			.selectFrom("ordini")
			.select("ordine_id")
			.where("ordine_id", "=", ordineId)
			.where("isActive", "=", false)
			.executeTakeFirst();

		if (!ordine) {
			console.error("Ordine not found:", ordineId);
			return false;
		}

		const payment = await db
			.selectFrom("payments")
			.select("id")
			.where("ordine_id", "=", ordineId)
			.where("paymentstatus", "=", PaymentStatusEnum.success)
			.executeTakeFirst();

		if (!payment) {
			return true;
		}
		console.error("Payment already exists for ordine:", ordineId);
		return false;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error checking ordine payment status: ${(e as Error).message}`,
		});
	}
};
export const getServizioPagamentoData = async (ordineId: OrdiniOrdineId) => {
	try {
		await db
			.deleteFrom("payments")
			.where("ordine_id", "=", ordineId)
			.where("paymentstatus", "is", null)
			.execute();

		const ordine = await db
			.selectFrom("ordini")
			.selectAll()
			.where("ordine_id", "=", ordineId)
			.executeTakeFirstOrThrow();

		const pack = await db
			.selectFrom("prezziario")
			.selectAll()
			.where("idprezziario", "=", ordine.packid)
			.executeTakeFirstOrThrow();

		const payment = await db
			.insertInto("payments")
			.values({
				amount_cent: pack.prezzo_cent,
				ordine_id: ordineId,
				paymentname: pack.nome_it,
				servizio_id: ordine.servizio_id,
				userid: ordine.userid,
			})
			.returningAll()
			.executeTakeFirstOrThrow();

		const servizio = await db
			.selectFrom("servizio")
			.selectAll()
			.where("servizio.servizio_id", "=", payment.servizio_id)
			.executeTakeFirstOrThrow();

		if (!servizio) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Servizio not found",
			});
		}

		return {
			ordine,
			payment,
			servizio,
		};
	} catch (e) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: `Servizio not found: ${(e as Error).message}`,
		});
	}
};

export const getPagamentoDataForCheckout = async (paymentId: PaymentsId) => {
	try {
		return await db
			.selectFrom("payments")
			.selectAll("payments")
			.innerJoin("users", "users.id", "payments.userid")
			.select("users.email")
			.innerJoin("ordini", "ordini.ordine_id", "payments.ordine_id")
			.innerJoin("prezziario", "prezziario.idprezziario", "ordini.packid")
			.select(["prezziario.nome_it", "prezziario.desc_it"])
			.where("payments.id", "=", paymentId)
			.executeTakeFirstOrThrow();
	} catch (e) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: `Payment not found: ${(e as Error).message}`,
		});
	}
};

type Pagamento = Payments & Pick<Users, "username">;
export const getAllPagamenti = async (): Promise<Pagamento[]> => {
	try {
		return await db
			.selectFrom("payments")
			.selectAll()
			.innerJoin("users", "users.id", "payments.userid")
			.select("users.username")
			.orderBy("payments.created_at", "desc")
			.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching user pagamenti: ${(e as Error).message}`,
		});
	}
};

export const updatePagamento = async ({
	paymentId,
	data,
}: {
	paymentId: PaymentsId;
	data: PaymentsUpdate;
}) => {
	try {
		return await db
			.updateTable("payments")
			.set(data)
			.where("id", "=", paymentId)
			.returningAll()
			.executeTakeFirstOrThrow();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error updating pagamento: ${(e as Error).message}`,
		});
	}
};

export const removePagamento = async (paymentId: PaymentsId) => {
	try {
		await db.deleteFrom("payments").where("id", "=", paymentId).execute();

		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error removing pagamento: ${(e as Error).message}`,
		});
	}
};
export const getUserPagamenti = async (
	userId: UsersId,
): Promise<Pagamento[]> => {
	try {
		return await db
			.selectFrom("payments")
			.selectAll()
			.select([sql.lit("").as("username")])
			.where("payments.userid", "=", userId)
			.orderBy("payments.created_at", "desc")
			.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching user pagamenti: ${(e as Error).message}`,
		});
	}
};

export const SendContactEmail = async ({
	codice,
	nome,
	email,
	telefono,
	messaggio,
}: {
	codice: string;
	nome: string;
	email: string;
	telefono: string;
	messaggio: string;
}) => {
	try {
		await NewMail({
			template: {
				mailType: "contattoAnnuncioEmail",
				props: {
					codice,
					email,
					messaggio,
					nome,
					telefono,
				},
			},
			mail: {
				subject: "Contatto per annuncio",
				to: "web@infoalloggi.it",
			},
		});
		return {
			email,
			nome,
			telefono,
		};
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error sending email: ${(e as Error).message}`,
		});
	}
};

export const getCompatibileAnnunci = async ({
	servizioId,
	adminOverride = false,
}: {
	servizioId: ServizioServizioId;
	adminOverride?: boolean;
}): Promise<(AnnuncioRicerca & { alreadyRequested: boolean })[]> => {
	try {
		const servizio = await getServizioById(servizioId);
		if (!servizio) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Servizio not found",
			});
		}

		const interests = await GetUserInterests(servizio.user_id);

		let qry = db
			.selectFrom("annunci")
			.distinctOn("annunci.id")
			.select((eb) => [
				"id",
				"codice",
				"comune",
				"provincia",
				"mq",
				"prezzo",
				"desc_en",
				"desc_it",
				"titolo_en",
				"titolo_it",
				"tipo",
				"consegna",
				"stato",
				"web",
				"media_updated_at",
				"homepage",
				"numero_camere",
				"external_videos",
				"modificato_il",
				withImages(),
				withVideos(),
				eb
					.case()
					.when("id", "in", interests)
					.then(true)
					.else(false)
					.end()
					.as("alreadyRequested"),
			])
			.where("web", "=", true)
			.where("stato", "!=", "Sospeso")
			.where("tipo", "=", servizio.tipologia)
			.where("id", "not in", (eb) =>
				eb
					.selectFrom("servizio_annunci")
					.select("annunci_id")
					.where("servizio_id", "=", servizioId),
			);
		if (adminOverride) {
			return await qry.execute();
		}

		qry = qry.where("prezzo", "<=", servizio.budget * 1e2 * 1.2); // Allow 20% margin

		const month = new Date().getMonth() + 1; // Months are 0-indexed in JS
		if (servizio.entromese === month) {
			qry = qry.where((eb) =>
				eb.or([
					eb("consegna", "=", 0),
					eb("consegna", "=", servizio.entromese),
				]),
			);
		} else if (servizio.entromese === 0) {
			qry = qry.where((eb) =>
				eb.or([eb("consegna", "=", 0), eb("consegna", "=", month)]),
			);
		} else {
			qry = qry.where("consegna", "=", servizio.entromese);
		}

		return await qry.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching compatibile annunci: ${(e as Error).message}`,
		});
	}
};

export const getOrdineRicevutaData = async ({
	ordineId,
	ctx,
}: {
	ordineId: OrdiniOrdineId;
	ctx: Context;
}) => {
	try {
		const data = await db
			.selectFrom("payments")
			.select([
				"payments.id",
				"payments.userid",
				"payments.amount_cent",
				"payments.paymentname",
				"payments.created_at",
				"payments.paymentmethod",
			])
			.select((eb) => [
				jsonObjectFrom(
					eb
						.selectFrom("users")
						.whereRef("users.id", "=", "payments.userid")
						.select(["users.nome", "users.cognome"])

						.limit(1),
				).as("user"),
				jsonObjectFrom(
					eb
						.selectFrom("users_anagrafica")
						.whereRef("users_anagrafica.userid", "=", "payments.userid")
						.select([
							"users_anagrafica.via_residenza",
							"users_anagrafica.civico_residenza",
							"users_anagrafica.comune_residenza",
							"users_anagrafica.provincia_residenza",
							"users_anagrafica.cap_residenza",
							"users_anagrafica.nazione_residenza",
						])
						.limit(1),
				).as("anagrafica"),
			])
			.where("payments.ordine_id", "=", ordineId)
			.where("payments.paymentstatus", "=", PaymentStatusEnum.success)

			.executeTakeFirstOrThrow();

		if (!ctx.session) {
			throw new TRPCError({
				code: "UNAUTHORIZED",
				message: "User not authenticated",
			});
		}
		if (!ctx.session.isAdmin && data.userid !== ctx.session.id) {
			throw new TRPCError({
				code: "FORBIDDEN",
				message: "You do not have permission to access this ordine",
			});
		}

		if (!data || !data.user || !data.anagrafica) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Ordine not found",
			});
		}
		return {
			clienteIndirizzo: `${data.anagrafica.via_residenza} ${data.anagrafica.civico_residenza}\n${data.anagrafica.comune_residenza} (${data.anagrafica.provincia_residenza}) ${data.anagrafica.cap_residenza}\n${data.anagrafica.nazione_residenza}`,
			clienteNome: `${data.user.nome} ${data.user.cognome}`,
			date: data.created_at,
			items: [
				{
					description: data.paymentname,
					discount: 0, // Assuming no discount for simplicity
					price: data.amount_cent / 100, // Convert cents to euros
				},
			],
			paymentId: data.id,
			paymentMethod: data.paymentmethod,
		} as PurchaseData;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error fetching ordine ricevuta data: ${(e as Error).message}`,
		});
	}
};
