import { TRPCError } from "@trpc/server";
import { env } from "~/env";
import { getStorageUrl } from "~/lib/storage_utils";
import type {
	Annunci,
	AnnunciId,
	AnnunciUpdate,
} from "~/schemas/public/Annunci";
import type { ImagesRefs } from "~/schemas/public/ImagesRefs";
import type { ServizioServizioId } from "~/schemas/public/Servizio";
import type { VideosRefs } from "~/schemas/public/VideosRefs";
import { db } from "~/server/db";
import { withImages, withVideos } from "~/utils/kysely-helper";
import { revalidate } from "../utils/revalidationHelper";

// const ratelimit = new RateLimiterHandler({
//   windowSize: 10,
//   maxRequests: 10,
//   analytics: true,
// });
export type AnnunciWithMedia = Annunci & {
	images: Pick<ImagesRefs, "img" | "thumb">[];
	videos: Pick<VideosRefs, "video" | "thumb">[];
};

export const getAnnunciListHandler = async (): Promise<
	Pick<
		Annunci,
		"id" | "codice" | "titolo_it" | "comune" | "tipo" | "locatore" | "stato"
	>[]
> => {
	try {
		return await db
			.selectFrom("annunci")
			.select([
				"id",
				"codice",
				"titolo_it",
				"comune",
				"tipo",
				"locatore",
				"stato",
			])
			.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query getAllRaw: ${(e as Error).message}`,
		});
	}
};

export const getCodici_AnnunciHandler = async (): Promise<string[]> => {
	try {
		const codici = await db
			.selectFrom("annunci")
			.select("codice")
			.where("annunci.web", "=", true)
			.where("stato", "!=", "Sospeso")
			.execute();
		if (!codici) {
			return [];
		}

		return codici.map((c) => c.codice);
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query getCodici: ${(e as Error).message}`,
		});
	}
};

export const getAnnunciByCod = async ({
	cod,
}: {
	cod: string;
}): Promise<AnnunciWithMedia | null> => {
	try {
		const annuncio = await db
			.selectFrom("annunci")
			.selectAll()
			.select((_eb) => [withImages(), withVideos()])
			.where("annunci.web", "=", true)
			.where("codice", "=", cod)
			.executeTakeFirst();

		if (!annuncio) {
			return null;
		}

		return annuncio;
	} catch (e) {
		throw new TRPCError({
			cause: (e as Error).cause,
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query getAnnunciByCod: ${(e as Error).message}`,
		});
	}
};
export const getAnnunciMetaByCod = async ({ cod }: { cod: string }) => {
	try {
		const annuncio = await db
			.selectFrom("annunci")
			.select((_eb) => [
				"titolo_it",
				"desc_it",
				withImages(),
				"media_updated_at",
			])
			.where("annunci.web", "=", true)
			.where("codice", "=", cod)
			.executeTakeFirst();
		if (!annuncio) {
			return null;
		}
		const ogImage =
			annuncio.images && annuncio.images.length > 0 && annuncio.images[0]
				? getStorageUrl({
						storageId: annuncio.images[0].img,
						params: {
							cacheKey: annuncio.media_updated_at?.toISOString(),
							media: "image",
						},
						host: env.BASE_URL,
					})
				: `${env.BASE_URL}/og.jpg`;

		return {
			description: annuncio.desc_it || "",
			ogImage,
			ogUrl: `${env.BASE_URL}/annuncio/${cod}`,
			title: annuncio.titolo_it || "",
		};
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query getAnnunciMetaByCod: ${(e as Error).message}`,
		});
	}
};

export const getAnnunciById_rawImgUrls = async ({
	id,
}: {
	id: AnnunciId;
}): Promise<AnnunciWithMedia | null> => {
	try {
		const annuncio = await db
			.selectFrom("annunci")
			.selectAll()
			.select((_eb) => [withImages(), withVideos()])
			.where("id", "=", id)
			.executeTakeFirst();
		if (!annuncio) {
			throw new TRPCError({
				code: "NOT_FOUND",
				message: "Annuncio non trovato",
			});
		}

		return annuncio;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query getAnnuncioById : ${(e as Error).message}`,
		});
	}
};

export type AnnuncioRicercaWPosition = AnnuncioRicerca & {
	lat_secondario: string | null;
	lon_secondario: string | null;
};

export const get_AnnunciPositionsHandler = async ({
	tipologia,
	comune,
	consegna,
}: {
	tipologia?: string;
	comune?: string;
	consegna?: number;
}): Promise<AnnuncioRicercaWPosition[]> => {
	try {
		let query = db
			.selectFrom("annunci")
			.select((_eb) => [
				"id",
				"codice",
				"comune",
				"provincia",
				"prezzo",
				"consegna",
				"numero_camere",
				"mq",
				"tipo",
				"titolo_it",
				"titolo_en",
				"desc_en",
				"desc_it",
				"web",
				"modificato_il",
				"stato",
				"external_videos",
				"lon_secondario",
				"lat_secondario",
				"media_updated_at",
				"homepage",
				withImages(),
				withVideos(),
			])

			.where("web", "=", true)
			.where("stato", "!=", "Sospeso")
			.orderBy("id", "asc");

		if (tipologia && tipologia !== "") {
			query = query.where("tipo", "=", tipologia);
		}
		if (comune && comune !== "") {
			query = query.where("comune", "like", comune);
		}
		if (consegna) {
			query = query.where("consegna", "=", consegna);
		}

		const annunci = await query.execute();
		if (!annunci) {
			return [];
		}

		return annunci;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query getAnnunciPositions: ${(e as Error).message}`,
		});
	}
};

export type AnnuncioRicerca = Pick<
	Annunci,
	| "id"
	| "codice"
	| "comune"
	| "provincia"
	| "prezzo"
	| "consegna"
	| "numero_camere"
	| "mq"
	| "tipo"
	| "titolo_it"
	| "titolo_en"
	| "desc_en"
	| "desc_it"
	| "modificato_il"
	| "stato"
	| "external_videos"
	| "media_updated_at"
	| "homepage"
	| "web"
> & {
	images: Pick<ImagesRefs, "img" | "thumb">[];
	videos: Pick<VideosRefs, "video" | "thumb">[];
};

export const getAnnunciRicerca = async ({
	tipologia,
	comune,
	consegna,
	sort,
}: {
	tipologia?: string;
	comune?: string;
	consegna?: number;
	sort?: "Recenti" | "Prezzo" | "Consegna" | "Mq";
}): Promise<AnnuncioRicerca[]> => {
	try {
		let query = db
			.selectFrom("annunci")
			.select((_eb) => [
				"id",
				"codice",
				"comune",
				"provincia",
				"prezzo",
				"consegna",
				"numero_camere",
				"mq",
				"tipo",
				"titolo_it",
				"titolo_en",
				"desc_en",
				"desc_it",
				"web",
				"modificato_il",
				"stato",
				"external_videos",
				"media_updated_at",
				"homepage",
				withImages(),
				withVideos(),
			])

			.where("web", "=", true)
			.where("stato", "!=", "Sospeso");

		if (tipologia && tipologia !== "") {
			query = query.where("tipo", "=", tipologia);
		}
		if (comune && comune !== "") {
			query = query.where("comune", "like", comune);
		}
		if (consegna) {
			query = query.where("consegna", "=", consegna);
		}

		switch (sort) {
			case "Recenti":
				query = query.orderBy("modificato_il", "desc");
				break;
			case "Prezzo":
				query = query.orderBy("prezzo", "asc");
				break;
			case "Consegna":
				query = query.orderBy("consegna", "asc");
				break;
			case "Mq":
				query = query.orderBy("mq", "asc");
				break;
			case undefined:
				break;
		}

		query = query.orderBy("id", "asc");

		return await query.execute();
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query ricercaAnnunci: ${(e as Error).message}`,
		});
	}
};

export const getOptions_AnnunciHandler = async () => {
	try {
		const comuni = await db
			.selectFrom("annunci")
			.select(["comune", "tipo", "consegna"])
			.where("web", "=", true)
			.where("stato", "!=", "Sospeso")
			.distinct()
			.execute();
		const results: { comune: string; tipo: string; consegna: number }[] = [];

		for (const comune of comuni) {
			if (comune.comune && comune.tipo && comune.consegna) {
				results.push({
					comune: comune.comune,
					consegna: comune.consegna,
					tipo: comune.tipo,
				});
			}
		}

		return results;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore query getComuni: ${(e as Error).message}`,
		});
	}
};

export const editAnnuncioHandler = async ({
	annuncioId,
	data,
}: {
	annuncioId: AnnunciId;
	data: AnnunciUpdate;
}) => {
	try {
		const annuncio = await db
			.updateTable("annunci")
			.set({
				...data,
				modificato_il: new Date(),
			})
			.where("id", "=", annuncioId)
			.returning("codice")
			.executeTakeFirstOrThrow();
		await revalidate(`/annuncio/${annuncio.codice}`);
		return true;
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore interno: ${(e as Error).message}`,
		});
	}
};

export const getProprietarioDataHandler = async ({
	annuncioId,
	servizioId,
}: {
	annuncioId: AnnunciId;
	servizioId: ServizioServizioId;
}) => {
	try {
		const opened_at = await db
			.selectFrom("servizio_annunci")
			.select("open_contatti_at")
			.where("servizio_id", "=", servizioId)
			.where("annunci_id", "=", annuncioId)
			.executeTakeFirst();
		if (!opened_at || !opened_at.open_contatti_at) {
			return { opened_at: null, propData: null };
		}

		const propData = await db
			.selectFrom("annunci")
			.select([
				"codice",
				"comune",
				"indirizzo",
				"civico",
				"provincia",
				"regione",
				"cap",
				"lat",
				"lon",
				"numero",
				"locatore",
			])
			.where("id", "=", annuncioId)
			.executeTakeFirstOrThrow();

		return { opened_at: opened_at.open_contatti_at, propData };
	} catch (e) {
		throw new TRPCError({
			code: "INTERNAL_SERVER_ERROR",
			message: `Errore recupero dati proprietario: ${(e as Error).message}`,
		});
	}
};
