import { z } from "zod";
import { Enums, prisma } from "#lib/server/prisma.js";
import { mollieClient } from "#lib/mollie.js";
import { PaymentMethod } from "@mollie/api-client";
import { APIError, defineApiRoute } from "astro-typesafe-api/server";
import {
	authorizationHeaders,
	authorizationMiddleware,
} from "#lib/middleware/authorization.js";
import { getAusweisartFromUUID, UUidWithPrefix } from "#components/Ausweis/types.js";
import { getPrismaAusweisAdapter } from "#lib/server/ausweis.js";
import { PRICES, SERVICES } from "#lib/constants.js";
import { Rechnung } from "#lib/client/prisma.js";
import { RechnungSchema } from "src/generated/zod/rechnung.js";

export const PUT = defineApiRoute({
	meta: {
		contentTypes: ["application/json"],
		description:
			"Erstellt eine neue Rechnung mit der ein Energieausweis bezahlt werden kann. Gibt eine Checkout URL zurück, welche der Nutzer folgen muss, um zum Kaufabschluss zu gelangen.",
		summary: "Erstellt eine neue Rechnung zu einem Energieausweis Vorgang.",
		tags: ["Rechnungen"],
	},
	input: z
		.object({
			ausweisart: z.nativeEnum(Enums.Ausweisart),
			ausweis_uid: UUidWithPrefix,
			ausweistyp: z.nativeEnum(Enums.AusweisTyp)
		})
		.merge(
			RechnungSchema.omit({
				benutzer_id: true,
				bezahlt_am: true,
				erstellt_am: true,
				id: true,
				status: true,
				uid: true,
				betrag: true,
				storniert_am: true,
				transaktions_referenz: true,
			})
		),
	output: z.object({
		checkout_url: z.string().optional(),
		uid: UUidWithPrefix,
	}),
	headers: authorizationHeaders,
	middleware: authorizationMiddleware,
	async fetch(input, ctx, user) {
		// Wir erstellen eine Mollie Payment Referenz und eine neue Rechnung in unserer Datenbank, daraufhin geben
		// wir eine Checkout URL zurück auf die der Nutzer weitergeleitet werden kann.

		const { ausweis_uid, ausweisart, bezahlmethode, services, ausweistyp } = input;
		let betrag = PRICES[ausweisart][ausweistyp]

		const servicePriceList = SERVICES[ausweisart]
		for (const service of input.services) {
			betrag += servicePriceList[service]
		}

		const adapter = getPrismaAusweisAdapter(ausweis_uid);

		if (!adapter) {
			throw new APIError({
				code: "BAD_REQUEST",
				message: "Ungültige Ausweis UID"
			})
		}

		const ausweis = await adapter.findUnique({
			where: {
				uid: ausweis_uid
			}
		})

		if (!ausweis) {
			throw new APIError({
				code: "NOT_FOUND",
				message: "Ausweis nicht gefunden.",
			});
		}

		if (ausweis.benutzer_id !== user.id) {
			throw new APIError({
				code: "UNAUTHORIZED",
				message: "Ausweis gehört nicht dem Nutzer.",
			});
		}

		// Wir erstellen eine neue Rechnung in unserer Datenbank.
		let rechnung: Rechnung | null = null;
		if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
			rechnung = await prisma.rechnung.create({
				data: {
					benutzer_id: user.id,
					betrag,
					bezahlmethode: bezahlmethode,
					status: Enums.Rechnungsstatus.open,
					verbrauchsausweis_wohnen: {
						connect: {
							uid: ausweis_uid
						}
					},
					services,
					ausweistyp
				}
			});
		} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
			rechnung = await prisma.rechnung.create({
				data: {
					benutzer_id: user.id,
					betrag,
					bezahlmethode: bezahlmethode,
					status: Enums.Rechnungsstatus.open,
					verbrauchsausweis_gewerbe: {
						connect: {
							uid: ausweis_uid
						}
					},
					services,
					ausweistyp
				}
			});
		} else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) {
			rechnung = await prisma.rechnung.create({
				data: {
					benutzer_id: user.id,
					betrag,
					bezahlmethode: bezahlmethode,
					status: Enums.Rechnungsstatus.open,
					bedarfsausweis_wohnen: {
						connect: {
							uid: ausweis_uid
						}
					},
					services,
					ausweistyp
				}
			});
		}

		if (!rechnung) {
			throw new APIError({
				code: "INTERNAL_SERVER_ERROR",
				message: "Rechnung konnte nicht erstellt werden.",
			});
		}

		await adapter.update({
			where: {
				uid: ausweis_uid
			},
			data: {
				bestellt: true
			}
		})

		if (bezahlmethode === Enums.Bezahlmethoden.rechnung) {
			return { uid: rechnung.uid }
		}

		// Wir erstellen eine Mollie Payment Referenz.
		const payment = await mollieClient.payments.create({
			amount: {
				value: betrag.toFixed(2),
				currency: "EUR",
			},
			metadata: {
				rechnung_uid: rechnung.uid,
			},
			method: input.bezahlmethode as PaymentMethod,
			description: "Verbrauchsausweis Wohnen 2016",
			redirectUrl: `https://ibcornelsen.de/payment/success?a=${ausweis.uid}&r=${rechnung.uid}`,
			webhookUrl: `http://ibcornelsen.de/api/webhooks/mollie?uid=${rechnung.uid}`,
		});

		const checkoutUrl = payment.getCheckoutUrl();

		if (!checkoutUrl) {
			throw new APIError({
				code: "INTERNAL_SERVER_ERROR",
				message: "Checkout URL konnte nicht erstellt werden.",
			});
		}

		return {
			uid: rechnung.uid,
			checkout_url: checkoutUrl,
		};
	},
});


