import { TRPCError } from "@trpc/server";
import { add, format } from "date-fns";
import { env } from "~/env";
import stripe from "~/lib/stripe";
import OrderTypeEnum from "~/schemas/public/OrderTypeEnum";
import type { Ordini, OrdiniOrdineId } from "~/schemas/public/Ordini";
import PaymentStatusEnum from "~/schemas/public/PaymentStatusEnum";
import type { TestiEStringheStingaId } from "~/schemas/public/TestiEStringhe";
import TipologiaPosizioneEnum from "~/schemas/public/TipologiaPosizioneEnum";
import { db } from "~/server/db";
import { NewMail } from "~/server/services/mailer";
import { CONDIZIONI_DEFAULT } from "~/server/services/prezziario.service";
import { getUser } from "~/server/services/user.service";
import { addEventToQueue } from "./event_queue.controller";

export const stripeHealthCheck = async () => {
	try {
		const endpoints = await stripe.webhookEndpoints.list();
		return {
			success: true,
			endpoints,
			secretKey: env.STRIPE_SECRET_KEY,
			publicKey: env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY,
		};
	} catch (e) {
		console.error("Stripe health check failed:", e);
		return {
			success: false,
			endpoints: null,
			secretKey: env.STRIPE_SECRET_KEY,
			publicKey: env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY,
		};
	}
};

export const whIntentCreatedHandler = async ({
	pIntentId,
	ordineId,
}: {
	pIntentId: Ordini["intent_id"];
	ordineId: OrdiniOrdineId;
}) => {
	try {
		return await db
			.updateTable("ordini")
			.set({
				intent_id: pIntentId,
				paymentstatus: PaymentStatusEnum.processing,
			})
			.where("ordine_id", "=", ordineId)
			.returning("ordine_id")
			.execute();
	} catch (error) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error whIntentCreatedHandler: ${(error as Error).message}`,
		});
	}
};

export const whIntentSucceededHandler = async ({
	pIntentId,
	ordineId,
	pm_id,
}: {
	pIntentId: Ordini["intent_id"];
	ordineId: OrdiniOrdineId;
	pm_id: string;
}) => {
	try {
		const paymentMethod = await stripe.paymentMethods.retrieve(pm_id);

		await db.transaction().execute(async (trx) => {
			const ordine = await trx
				.updateTable("ordini")
				.set({
					paid_at: new Date(),
					paymentmethod: paymentMethod.type,
					paymentstatus: PaymentStatusEnum.success,
					isActive: true,
				})
				.where("intent_id", "=", pIntentId)
				.where("ordine_id", "=", ordineId)
				.returningAll()
				.executeTakeFirstOrThrow(() => {
					throw new Error(
						`Payment not found - intent_id: ${pIntentId}, ordineId: ${ordineId}`,
					);
				});
			const condizioniFilename = `condizioni_contrattuali_${(ordine.paid_at || ordine.created_at).toISOString()}.pdf`;
			const ricevutaFilename = `ricevuta_cortesia_${(ordine.paid_at || ordine.created_at).toISOString()}.pdf`;
			const condizioni = await trx
				.selectFrom("prezziario")
				.select("testo_condizioni")
				.where("idprezziario", "=", ordine.packid)
				.executeTakeFirst();

			const utente = await getUser({
				db: trx,
				userId: ordine.userid,
			});

			const lock_expires = add(new Date(), { seconds: 30 }); // Lock for 30 sec

			switch (ordine.type) {
				case OrderTypeEnum.Acconto: {
					if (!ordine.servizio_id) {
						throw new Error(
							`No servizio_id for ordine_id: ${ordine.ordine_id}`,
						);
					}

					const servizio = await trx
						.updateTable("servizio")
						.where("servizio_id", "=", ordine.servizio_id)
						.set({
							decorrenza: new Date(),
							isOkAcconto: true,
						})
						.returning(["interruzioneDays"])
						.executeTakeFirstOrThrow(
							() =>
								new Error(
									`Failed to update servizio - servizioId: ${ordine.servizio_id}`,
								),
						);
					if (utente.comms_enabled) {
						await addEventToQueue({
							data: {
								tipo: "email",
								data: {
									template: {
										mailType: "servizioAttivato",
									},
									mail: {
										subject: "Pagamento Confermato - Acconto Infoalloggi.it",
										to: utente.email,
										attachments: [
											{
												filename: condizioniFilename,
												generate: {
													type: "condizioni",
													stringId: (condizioni?.testo_condizioni ||
														CONDIZIONI_DEFAULT) as TestiEStringheStingaId,
												},
												encoding: "base64",
												contentType: "application/pdf",
											},
											{
												filename: ricevutaFilename,
												generate: {
													type: "ricevuta_cortesia",
													ordineId: ordine.ordine_id,
												},
												encoding: "base64",
												contentType: "application/pdf",
											},
										],
									},
									userId: utente.id,
								},
							},
							lock_expires,
						});

						await addEventToQueue({
							data: {
								tipo: "email",
								data: {
									template: {
										mailType: "avvisoInterruzione",
									},
									mail: {
										subject: "Promemoria durata servizio - Infoalloggi.it",
										to: utente.email,
									},
									userId: utente.id,
								},
							},
							lock_expires: add(new Date(), {
								days: servizio.interruzioneDays - 5,
							}),
						});
					}
					//SERVIZIO ATTIVATO

					break;
				}

				case OrderTypeEnum.Saldo: {
					if (!ordine.servizio_id) {
						throw new Error(
							`No servizio_id for ordine_id: ${ordine.ordine_id}`,
						);
					}
					const servizio = await trx
						.selectFrom("servizio")
						.select(["servizio.tipologia"])
						.where("servizio_id", "=", ordine.servizio_id)
						.executeTakeFirstOrThrow(() => {
							throw new Error(
								`Servizio not found - servizio_id: ${ordine.servizio_id}`,
							);
						});

					await trx
						.updateTable("servizio")
						.where("servizio_id", "=", ordine.servizio_id)
						.set({
							isOkConsulenza:
								servizio.tipologia === TipologiaPosizioneEnum.Transitorio,
							isOkSaldo: true,
						})
						.execute();
					if (utente.comms_enabled) {
						await addEventToQueue({
							data: {
								tipo: "email",
								data: {
									template: {
										mailType: "pagamentoConferma",
									},
									mail: {
										subject: "Pagamento Confermato - Saldo Infoalloggi.it",
										to: utente.email,
										attachments: [
											{
												filename: ricevutaFilename,
												generate: {
													type: "ricevuta_cortesia",
													ordineId: ordine.ordine_id,
												},
												encoding: "base64",
												contentType: "application/pdf",
											},
										],
									},
									userId: utente.id,
								},
							},
							lock_expires,
						});
					}
					break;
				}
				case OrderTypeEnum.Rinnovo: {
					// nessuna azione aggiuntiva richiesta
					if (utente.comms_enabled) {
						await addEventToQueue({
							data: {
								tipo: "email",
								data: {
									template: {
										mailType: "pagamentoConferma",
									},
									mail: {
										subject: "Pagamento Confermato - Rinnovo Infoalloggi.it",
										to: utente.email,
										attachments: [
											{
												filename: ricevutaFilename,
												generate: {
													type: "ricevuta_cortesia",
													ordineId: ordine.ordine_id,
												},
												encoding: "base64",
												contentType: "application/pdf",
											},
										],
									},
									userId: utente.id,
								},
							},
							lock_expires,
						});
					}
					break;
				}
				case OrderTypeEnum.Consulenza: {
					if (!ordine.servizio_id) {
						throw new Error(
							`No servizio_id for ordine_id: ${ordine.ordine_id}`,
						);
					}

					await trx
						.updateTable("servizio")
						.where("servizio_id", "=", ordine.servizio_id)
						.set({
							isOkConsulenza: true,
						})
						.execute();
					if (utente.comms_enabled) {
						await addEventToQueue({
							data: {
								tipo: "email",
								data: {
									template: {
										mailType: "pagamentoConferma",
									},
									mail: {
										subject: "Pagamento Confermato - Consulenza Infoalloggi.it",
										to: utente.email,
										attachments: [
											{
												filename: ricevutaFilename,
												generate: {
													type: "ricevuta_cortesia",
													ordineId: ordine.ordine_id,
												},
												encoding: "base64",
												contentType: "application/pdf",
											},
										],
									},
									userId: utente.id,
								},
							},
							lock_expires,
						});
					}
					break;
				}
				case OrderTypeEnum.Altro:
					break;
			}
			await addEventToQueue({
				data: {
					tipo: "email",
					data: {
						template: {
							mailType: "generic",
							props: {
								title: "Nuovo pagamento ricevuto",
								testo: `È stato ricevuto un nuovo pagamento.`,
							},
						},
						mail: {
							subject: "Ric pagamento",
							to: "web@infoalloggi.it",
							attachments: [
								{
									filename: `ricevuta_cortesia_${utente.username.replace(/\s+/g, "-")}-${format(ordine.created_at, "dd-MM-yyyy")}.pdf`,

									generate: {
										type: "ricevuta_cortesia",
										ordineId: ordine.ordine_id,
									},
									encoding: "base64",
									contentType: "application/pdf",
								},
								{
									filename: condizioniFilename,
									generate: {
										type: "condizioni",
										stringId: (condizioni?.testo_condizioni ||
											CONDIZIONI_DEFAULT) as TestiEStringheStingaId,
									},
									encoding: "base64",
									contentType: "application/pdf",
								},
							],
						},
					},
				},
				lock_expires,
			});
		});
		return true;
	} catch (error) {
		console.error("whIntentSucceededHandler error:", {
			pIntentId,
			ordineId,
			pm_id,
			error: error instanceof Error ? error.message : String(error),
			stack: error instanceof Error ? error.stack : undefined,
		});
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Error updating payment intent: ${(error as Error).message}`,
			cause: error,
		});
	}
};

export const whIntentFailedHandler = async ({
	pIntentId,
}: {
	pIntentId: Ordini["intent_id"];
}) => {
	const userId = await db
		.updateTable("ordini")
		.set({
			paymentstatus: PaymentStatusEnum.failed,
		})
		.returning("userid")
		.where("paymentstatus", "=", PaymentStatusEnum.processing)
		.where("intent_id", "=", pIntentId)
		.execute();

	if (userId[0]) {
		const utente = await getUser({
			db,
			userId: userId[0].userid,
		});

		await NewMail({
			template: {
				mailType: "pagamentoErrore",
			},
			mail: {
				subject: "Pagamento Fallito - Infoalloggi.it",
				to: utente.email,
			},
			userId: utente.id,
		});
	}
	return true;
};
