import { TRPCError } from "@trpc/server";
import { format } from "date-fns";
import { jsonArrayFrom } from "kysely/helpers/postgres";
import {
	ITALY_CATASTO_CODE,
	parseDBComune,
	parseDBNazione,
} from "~/lib/catasto";
import type { Etichette } from "~/schemas/public/Etichette";
import type { Users, UsersId, UsersUpdate } from "~/schemas/public/Users";
import type { UsersAnagraficaUpdate } from "~/schemas/public/UsersAnagrafica";
import { getComuni, getNazioni } from "~/server/controllers/catasto.controller";
import { db, type Querier } from "~/server/db";
import { startsWithVowel, titleCase } from "~/utils/utils";

export const editAccountHandler = async ({
	id,
	data,
}: {
	id: UsersId;
	data: UsersUpdate;
}) => {
	try {
		await db
			.updateTable("users")
			.set({
				...data,
			})
			.where("id", "=", id)
			.execute();

		return "success";
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `errore modifica utente - id: ${id}: ${(e as Error).message}`,
		});
	}
};

export const editAnagraficaHandler = async ({
	userId,
	data,
}: {
	userId: UsersId;
	data: UsersAnagraficaUpdate;
}) => {
	try {
		const { idanagrafica, userid, ...rest } = data;
		if (!userId) {
			throw new TRPCError({
				code: "BAD_REQUEST",
				message: `Errore interno, utente non trovato - userId: ${userId}`,
			});
		}

		return await db
			.insertInto("users_anagrafica")
			.values({
				...rest,
				userid: userId,
			})
			.onConflict((oc) =>
				oc.column("userid").doUpdateSet({
					...rest,
				}),
			)
			.returningAll()
			.executeTakeFirst();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore updateAnagrafica - userId: ${userId}: ${(e as Error).message}`,
		});
	}
};

export const deleteUserHandler = async (id: UsersId) => {
	try {
		await db.deleteFrom("users").where("id", "=", id).execute();
		return { success: true };
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `errore cancellazione utente - id: ${id}: ${(e as Error).message}`,
		});
	}
};

export const blockUserHandler = async (id: UsersId) => {
	return await db.transaction().execute(async (trx) => {
		return await trx
			.updateTable("users")
			.set((eb) => ({
				isBlocked: eb.not("isBlocked"),
			}))
			.where("id", "=", id)
			.where("isAdmin", "=", false)
			.returningAll()
			.execute();
	});
};

export const getUserHandler = async ({
	db,
	id,
}: {
	db: Querier;
	id: UsersId;
}) => {
	try {
		const user = await db
			.selectFrom("users")
			.leftJoin("users_anagrafica", "users_anagrafica.userid", "users.id")
			.where("id", "=", id)
			.selectAll()
			.executeTakeFirst();

		if (!user) {
			return null;
		}
		return user;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `errore get utente - id: ${id}: ${(e as Error).message}`,
		});
	}
};
export type UserWAnagrafica = Awaited<ReturnType<typeof getUserHandler>>;

export type NonNullableUser = {
	id: NonNullable<UsersId>;
	username: NonNullable<string>;
	nome: string | null;
};
export const getActiveUserWithoutChatHandler = async ({
	db,
}: {
	db: Querier;
}) => {
	try {
		const users = await db
			.selectFrom("users")
			.fullJoin("chats", "chats.user_ref", "users.id")
			.select(["users.id", "users.username", "users.nome"])
			.where("chats.chatid", "is", null)
			.where("users.isBlocked", "=", false)
			.where("users.isAdmin", "=", false)
			.execute();
		return users.filter((user): user is NonNullableUser => {
			return user.id !== null && user.username !== null;
		});
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `errore getActiveUserWithoutChatHandler: ${(e as Error).message}`,
		});
	}
};

export type UserWChatInfo = Pick<
	Users,
	| "id"
	| "nome"
	| "username"
	| "email"
	| "telefono"
	| "extra_telefono_numero"
	| "extra_telefono_titolo"
	| "isAdmin"
	| "isBlocked"
	| "created_at"
	| "note"
> & { chatid: string | null; etichette: Etichette[] };

export const getUsersWithChatInfoHandler = async (): Promise<
	UserWChatInfo[]
> => {
	try {
		return await db
			.selectFrom("users")
			.select([
				"users.id",
				"users.nome",
				"users.username",
				"users.telefono",
				"users.extra_telefono_numero",
				"users.extra_telefono_titolo",
				"users.email",
				"users.isAdmin",
				"users.isBlocked",
				"users.created_at",
				"users.note",
			])
			.leftJoin("chats", "chats.user_ref", "users.id")
			.select(["chats.chatid"])
			.select((eb) =>
				jsonArrayFrom(
					eb
						.selectFrom("etichette")
						.innerJoin(
							"user_etichette",
							"user_etichette.etichettaId",
							"etichette.id_etichetta",
						)
						.whereRef("user_etichette.userId", "=", "users.id")
						.select([
							"etichette.id_etichetta",
							"etichette.title",
							"etichette.color_hex",
						]),
				).as("etichette"),
			)
			.orderBy("users.id", "desc")
			.select(["users.id as id"])
			.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `errore getUsersWithChatInfoHandler: ${(e as Error).message}`,
		});
	}
};

export const genUserIntestazione = async (userId: UsersId) => {
	try {
		const data = await db
			.selectFrom("users")
			.innerJoin("users_anagrafica", "users_anagrafica.userid", "users.id")
			.where("id", "=", userId)
			.select([
				"users.cognome",
				"users.nome",
				"users_anagrafica.data_nascita",
				"users_anagrafica.luogo_nascita",
				"users_anagrafica.nazione_nascita",
				"users_anagrafica.sesso",
				"users_anagrafica.via_residenza",
				"users_anagrafica.civico_residenza",
				"users_anagrafica.comune_residenza",
				"users_anagrafica.provincia_residenza",
				"users_anagrafica.cap_residenza",
				"users_anagrafica.codice_fiscale",
			])
			.executeTakeFirstOrThrow(
				() => new Error(`User not found - userId: ${userId}`),
			);

		if (
			!data.sesso ||
			!data.cognome ||
			!data.nome ||
			!data.data_nascita ||
			!data.luogo_nascita ||
			!data.nazione_nascita ||
			!data.comune_residenza ||
			!data.via_residenza ||
			!data.codice_fiscale
		) {
			throw new Error(
				`Dati anagrafici incompleti per generazione intestazione - userId: ${userId}`,
			);
		}

		const comuni = await getComuni();
		const nazioni = await getNazioni();

		const parsedComuneResidenza = parseDBComune(data.comune_residenza, comuni);
		const parsedComuneNascita = parseDBComune(data.luogo_nascita, comuni);
		const parsedNazione = parseDBNazione(data.nazione_nascita, nazioni);

		const comuneNascita = parsedComuneNascita
			? titleCase(parsedComuneNascita.nome)
			: data.luogo_nascita;
		const comuneResidenza = parsedComuneResidenza
			? titleCase(parsedComuneResidenza.nome)
			: data.comune_residenza;
		const nazioneNascita = parsedNazione ? titleCase(parsedNazione.nome) : "";

		// Determine article
		const isMale = data.sesso === "M";
		const articolo = isMale ? "Al Sig." : "Alla Sig.ra";
		const natoNata = isMale ? "nato" : "nata";
		const preposizione1 = startsWithVowel(comuneNascita) ? "ad" : "a";
		const preposizione2 = startsWithVowel(comuneResidenza) ? "ad" : "a";

		// Build phrase
		const frase = [
			`${articolo} ${data.cognome.toUpperCase()} ${data.nome.toUpperCase()}`,
			`${natoNata} il ${format(data.data_nascita, "dd/MM/yyyy")}`,
			`${preposizione1} ${comuneNascita}${parsedNazione?.catasto === ITALY_CATASTO_CODE ? (parsedComuneNascita ? ` (${parsedComuneNascita.sigla})` : "") : ` (${nazioneNascita})`},`,
			`residente in ${titleCase(data.via_residenza)}, ${data.civico_residenza}`,
			`${preposizione2} ${comuneResidenza} (${data.provincia_residenza})`,
			`CAP ${data.cap_residenza},`,
			`con codice fiscale: ${data.codice_fiscale}`,
		].join(" ");
		return frase;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore genUserIntestazione - userId: ${userId}: ${(e as Error).message}`,
		});
	}
};
