import { randomBytes } from "node:crypto";
import { TRPCError } from "@trpc/server";
import { add } from "date-fns";
import { env } from "~/env";
import type { UsersId } from "~/schemas/public/Users";
import { db } from "~/server/db";
import { NewMail } from "~/server/services/mailer";
import { generateSalt, hashPassword } from "~/server/services/password.service";
import { findUser_byEmail } from "~/server/services/user.service";

const reset_password_token_validity = 30; // minutes
export const genPasswordResetToken = async ({ id }: { id: UsersId }) => {
	const reset_expiry = add(new Date(), {
		minutes: reset_password_token_validity,
	});

	const newToken = randomBytes(20).toString("hex");
	try {
		await db
			.updateTable("users")
			.set({
				reset_password_expires: reset_expiry,
				reset_password_token: newToken,
			})
			.where("id", "=", id)
			.execute();
		return newToken;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `genPasswordResetToken: Errore interno: ${(e as Error).message}`,
		});
	}
};

export const isTokenValid = async ({ token }: { token: string }) => {
	try {
		const user = await db
			.selectFrom("users")
			.select("id")
			.where("reset_password_token", "=", token)
			.where("reset_password_expires", ">", new Date())
			.executeTakeFirst();

		if (!user) {
			return false;
		}
		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `isTokenValid: Errore interno: ${(e as Error).message}`,
		});
	}
};

export const resetPasswordFromTokenHandler = async ({
	token,
	newpassword,
}: {
	token: string;
	newpassword: string;
}) => {
	try {
		const user = await db
			.selectFrom("users")
			.select("id")
			.where("reset_password_token", "=", token)
			.where("reset_password_expires", ">", new Date())
			.executeTakeFirst();

		if (!user) {
			throw new TRPCError({
				code: "BAD_REQUEST",
				message: "Token non valido",
			});
		}
		const salt = generateSalt();
		const hashedPassword = await hashPassword(newpassword, salt);
		await db
			.updateTable("users")
			.set({
				password: hashedPassword,
				reset_password_expires: null,
				reset_password_token: null,
				salt,
			})
			.where("id", "=", user.id)
			.execute();
		return "success";
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Password Reset: Errore interno: ${(e as Error).message}`,
		});
	}
};

export const pwResetSendLinkHandler = async ({ email }: { email: string }) => {
	const user = await findUser_byEmail({ email });
	if (!user) {
		throw new TRPCError({
			code: "NOT_FOUND",
			message: `Password Reset: Utente non trovato con email: ${email}`,
		});
	}

	const token = await genPasswordResetToken({ id: user.id });

	await NewMail({
		template: {
			mailType: "pwResetLink",
			props: {
				resetlink: `${env.BASE_URL}/auth/password-reset/${token}`,
			},
		},
		mail: {
			subject: `Richiesta di reset password Infoalloggi.it`,
			to: email,
		},
		userId: user.id,
	});
};

export const passwordOverrideHandler = async ({
	id,
	password,
}: {
	id: UsersId;
	password: string;
}) => {
	const salt = generateSalt();
	const hashedPassword = await hashPassword(password, salt);
	try {
		await db
			.updateTable("users")
			.set({
				password: hashedPassword,
				salt,
			})
			.where("id", "=", id)
			.execute();
		return {
			status: "success",
		};
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Password Override: Errore interno: ${(e as Error).message}`,
		});
	}
};
