import { logger } from "../logger/index.mjs";
import { ensureMongoDocumentToObject } from "../utils/ensureMongoDocumentToObject.mjs";
import { formatPaginatedResponse, formatResponse } from "../utils/responseData.mjs";
import { ErrorHandler } from "../utils/errors/ErrorHandler.mjs";
import { getProjectById } from "../services/project.service.mjs";
import { countDictionaries, createDictionary, deleteDictionaryById, findDictionaries, getDictionaryById, getDictionaryByKey as getDictionaryByKey$1, incrementVersion, updateDictionaryById, updateDictionaryByKey } from "../services/dictionary.service.mjs";
import { sendDictionaryUpdate } from "./eventListener.controller.mjs";
import { triggerAll } from "../services/webhook.service.mjs";
import { getDictionaryFiltersAndPagination } from "../utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs";
import { mapDictionaryToAPI } from "../utils/mapper/dictionary.mjs";
import { hasPermission } from "../utils/permissions.mjs";
import { t } from "fastify-intlayer";
import { isDeepStrictEqual } from "node:util";

//#region src/controllers/dictionary.controller.ts
const removeMetadata = (obj) => {
	if (Array.isArray(obj)) return obj.map(removeMetadata);
	if (obj && typeof obj === "object") {
		const clone = {};
		for (const key in obj) if (key !== "metadata") clone[key] = removeMetadata(obj[key]);
		return clone;
	}
	return obj;
};
/**
* Retrieves a list of dictionaries based on filters and pagination.
*/
const getDictionaries = async (request, reply) => {
	const { user, project, roles } = request.locals || {};
	const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } = getDictionaryFiltersAndPagination(request);
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	try {
		const dictionaries = await findDictionaries({
			...filters,
			projectIds: project.id
		}, skip, pageSize, sortOptions);
		if (!hasPermission(roles || [], "dictionary:read")({
			...request.locals,
			targetDictionaries: dictionaries
		})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
		const totalItems = await countDictionaries(filters);
		const responseData = formatPaginatedResponse({
			data: dictionaries.map((el) => mapDictionaryToAPI(el)),
			page,
			pageSize,
			totalPages: getNumberOfPages(totalItems),
			totalItems
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Retrieves a list of dictionaries keys based on filters and pagination.
*/
const getDictionariesKeys = async (_request, reply) => {
	const { project, roles } = _request.locals || {};
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	try {
		const dictionaries = await findDictionaries({ projectIds: project.id });
		if (!hasPermission(roles || [], "dictionary:read")({
			..._request.locals,
			targetDictionaries: dictionaries
		})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
		const responseData = formatResponse({ data: dictionaries.map((dictionary) => dictionary.key) });
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Retrieves a list of dictionaries keys based on filters and pagination.
*/
const getDictionariesUpdateTimestamp = async (_request, reply) => {
	const { project, roles } = _request.locals || {};
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	try {
		const dictionaries = await findDictionaries({ projectIds: project.id });
		if (!hasPermission(roles || [], "dictionary:read")({
			..._request.locals,
			targetDictionaries: dictionaries
		})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
		const dictionariesUpdateTimestamp = {};
		for (const dictionary of dictionaries) dictionariesUpdateTimestamp[dictionary.id] = {
			key: dictionary.key,
			updatedAt: new Date(dictionary.updatedAt).getTime()
		};
		const responseData = formatResponse({ data: dictionariesUpdateTimestamp });
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Retrieves a list of dictionaries based on filters and pagination.
*/
const getDictionaryByKey = async (request, reply) => {
	const { project, user, roles } = request.locals || {};
	const { dictionaryKey } = request.params;
	const version = request.query.version;
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	try {
		const dictionary = await getDictionaryByKey$1(dictionaryKey, project.id);
		if (!hasPermission(roles || [], "dictionary:read")({
			...request.locals,
			targetDictionaries: [dictionary]
		})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
		if (!dictionary.projectIds.map(String).includes(String(project.id))) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_PROJECT_MISMATCH");
		const responseData = formatResponse({ data: mapDictionaryToAPI(dictionary, version) });
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Adds a new dictionary to the database.
*/
const addDictionary = async (request, reply) => {
	const { project, user, roles } = request.locals || {};
	const dictionaryData = request.body.dictionary;
	if (!dictionaryData) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_DATA_NOT_FOUND");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!dictionaryData.projectIds?.includes(String(project.id))) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_PROJECT_MISMATCH");
	const dictionary = {
		key: dictionaryData.key,
		title: dictionaryData.title,
		description: dictionaryData.description,
		content: new Map([["v1", { content: removeMetadata(dictionaryData.content ?? {}) }]]),
		creatorId: user.id,
		projectIds: dictionaryData.projectIds ?? [String(project.id)]
	};
	if (!hasPermission(roles || [], "dictionary:write")(request.locals)) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const newDictionary = await createDictionary(dictionary);
		const apiResult = mapDictionaryToAPI(newDictionary);
		const responseData = formatResponse({
			message: t({
				en: "Dictionary created successfully",
				fr: "Dictionnaire créé avec succès",
				es: "Diccionario creado con éxito"
			}),
			description: t({
				en: "Your dictionary has been created successfully",
				fr: "Votre dictionnaire a été créé avec succès",
				es: "Su diccionario ha sido creado con éxito"
			}),
			data: apiResult
		});
		sendDictionaryUpdate([{
			dictionary: mapDictionaryToAPI(newDictionary),
			status: "ADDED"
		}]);
		if (project) try {
			const fullProject = await getProjectById(project.id);
			await triggerAll(fullProject);
		} catch (error) {
			logger.error("Failed to trigger CI builds after dictionary creation", error);
		}
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Check each dictionaries, add the new ones and update the existing ones.
*/
const pushDictionaries = async (request, reply) => {
	const { project, user, roles } = request.locals || {};
	let dictionaryData = request.body.dictionaries;
	if (dictionaryData && !Array.isArray(dictionaryData) && typeof dictionaryData === "object" && "dictionaries" in dictionaryData && Array.isArray(dictionaryData.dictionaries)) dictionaryData = dictionaryData.dictionaries;
	if (typeof dictionaryData === "object" && Array.isArray(dictionaryData) && dictionaryData.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARIES_NOT_PROVIDED");
	else if (!dictionaryData) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_DATA_NOT_FOUND");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!hasPermission(roles || [], "dictionary:write")(request.locals)) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const existingDictionaries = dictionaryData.filter((dictionary) => dictionary.id !== void 0);
		const newDictionaries = dictionaryData.filter((dictionary) => dictionary.id === void 0);
		const newDictionariesResult = [];
		const updatedDictionariesResult = [];
		const errorResult = [];
		for (const dictionaryDataEl of newDictionaries) {
			const dictionary = {
				title: dictionaryDataEl.title,
				description: dictionaryDataEl.description,
				projectIds: [String(project.id)],
				creatorId: user.id,
				content: new Map([["v1", { content: removeMetadata(dictionaryDataEl.content) ?? {} }]]),
				key: dictionaryDataEl.key
			};
			try {
				const newDictionary = await createDictionary(dictionary);
				newDictionariesResult.push({
					key: newDictionary.key,
					localId: dictionaryDataEl.localId,
					id: newDictionary.id
				});
			} catch (error) {
				errorResult.push({
					id: dictionaryDataEl.id,
					key: dictionaryDataEl.key,
					localId: dictionaryDataEl.localId,
					message: error.message
				});
			}
		}
		for (const dictionaryDataEl of existingDictionaries) {
			const remoteDictionary = await getDictionaryById(dictionaryDataEl.id);
			const cleanedContent = removeMetadata(dictionaryDataEl.content);
			const versionList = [...remoteDictionary.content.keys() ?? []];
			const lastVersion = versionList[versionList.length - 1];
			const isSameContent = isDeepStrictEqual(remoteDictionary.content.get(lastVersion)?.content ?? null, cleanedContent);
			const newContent = new Map(remoteDictionary.content);
			if (!isSameContent) {
				const newContentVersion = incrementVersion(remoteDictionary);
				newContent.set(newContentVersion, { content: cleanedContent });
			}
			const dictionary = {
				...ensureMongoDocumentToObject(remoteDictionary),
				...dictionaryDataEl,
				content: newContent,
				projectIds: [String(project.id)],
				creatorId: user.id,
				key: remoteDictionary.key
			};
			try {
				const updatedDictionary = await updateDictionaryByKey(remoteDictionary.key, dictionary, project.id);
				updatedDictionariesResult.push({
					key: updatedDictionary.key,
					localId: dictionaryDataEl.localId,
					id: updatedDictionary.id
				});
			} catch (error) {
				errorResult.push({
					id: dictionaryDataEl.id,
					key: dictionaryDataEl.key,
					localId: dictionaryDataEl.localId,
					message: error.message
				});
			}
		}
		const result = {
			newDictionaries: newDictionariesResult,
			updatedDictionaries: updatedDictionariesResult,
			error: errorResult
		};
		const responseData = formatResponse({
			message: t({
				en: "Dictionaries updated successfully",
				fr: "Dictionnaires mis à jour avec succès",
				es: "Diccionarios actualizados con éxito"
			}),
			description: t({
				en: "Your dictionaries have been updated successfully",
				fr: "Vos dictionnaires ont été mis à jour avec succès",
				es: "Sus diccionarios han sido actualizados con éxito"
			}),
			data: result
		});
		sendDictionaryUpdate([...newDictionariesResult.map((dictionary) => ({
			dictionary,
			status: "ADDED"
		})), ...updatedDictionariesResult.map((dictionary) => ({
			dictionary,
			status: "UPDATED"
		}))]);
		if (project && (newDictionariesResult.length > 0 || updatedDictionariesResult.length > 0)) try {
			const fullProject = await getProjectById(project.id);
			await triggerAll(fullProject);
		} catch (error) {
			logger.error("Failed to trigger CI builds after dictionary push", error);
		}
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Updates an existing dictionary in the database.
*/
const updateDictionary = async (request, reply) => {
	const { dictionaryId } = request.params;
	const { project, roles } = request.locals || {};
	const dictionaryData = request.body;
	if (!dictionaryData) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_DATA_NOT_FOUND");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!dictionaryData.projectIds?.includes(String(project.id))) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_PROJECT_MISMATCH");
	if (typeof dictionaryId === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_ID_NOT_FOUND");
	if (!hasPermission(roles || [], "dictionary:write")(request.locals)) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const apiResult = mapDictionaryToAPI(await updateDictionaryById(dictionaryId, dictionaryData));
		const responseData = formatResponse({
			message: t({
				en: "Dictionary updated successfully",
				fr: "Dictionnaire mis à jour avec succès",
				es: "Diccionario actualizado con éxito"
			}),
			description: t({
				en: "Your dictionary has been updated successfully",
				fr: "Votre dictionnaire a été mis à jour avec succès",
				es: "Su diccionario ha sido actualizado con éxito"
			}),
			data: apiResult
		});
		sendDictionaryUpdate([{
			dictionary: apiResult,
			status: "UPDATED"
		}]);
		if (project) try {
			const fullProject = await getProjectById(project.id);
			await triggerAll(fullProject);
		} catch (error) {
			logger.error("Failed to trigger CI builds after dictionary update", error);
		}
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Deletes a dictionary from the database by its ID.
*/
const deleteDictionary = async (request, reply) => {
	const { project, roles } = request.locals || {};
	const { dictionaryId } = request.params;
	if (!dictionaryId) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_ID_NOT_FOUND");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!hasPermission(roles || [], "dictionary:admin")(request.locals)) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		if (!(await getDictionaryById(dictionaryId)).projectIds.includes(project.id)) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_PROJECT_MISMATCH");
		const deletedDictionary = await deleteDictionaryById(dictionaryId);
		if (!deletedDictionary) return ErrorHandler.handleGenericErrorResponse(reply, "DICTIONARY_NOT_FOUND", { dictionaryId });
		logger.info(`Dictionary deleted: ${String(deletedDictionary.id)}`);
		const apiResult = mapDictionaryToAPI(deletedDictionary);
		const responseData = formatResponse({
			message: t({
				en: "Dictionary deleted successfully",
				fr: "Dictionnaire supprimé avec succès",
				es: "Diccionario eliminado con éxito"
			}),
			description: t({
				en: "Your dictionary has been deleted successfully",
				fr: "Votre dictionnaire a été supprimé avec succès",
				es: "Su diccionario ha sido eliminado con éxito"
			}),
			data: apiResult
		});
		sendDictionaryUpdate([{
			dictionary: apiResult,
			status: "DELETED"
		}]);
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};

//#endregion
export { addDictionary, deleteDictionary, getDictionaries, getDictionariesKeys, getDictionariesUpdateTimestamp, getDictionaryByKey, pushDictionaries, updateDictionary };
//# sourceMappingURL=dictionary.controller.mjs.map