import { ensureMongoDocumentToObject } from "../utils/ensureMongoDocumentToObject.mjs";
import { logger } from "../logger/index.mjs";
import { formatPaginatedResponse, formatResponse } from "../utils/responseData.mjs";
import { ErrorHandler } from "../utils/errors/ErrorHandler.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 { getDictionaryFiltersAndPagination } from "../utils/filtersAndPagination/getDictionaryFiltersAndPagination.mjs";
import { mapDictionaryToAPI } from "../utils/mapper/dictionary.mjs";
import { hasPermission } from "../utils/permissions.mjs";
import { t } from "express-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 (req, res, _next) => {
	const { user, project, roles } = res.locals;
	const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } = getDictionaryFiltersAndPagination(req, res);
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	try {
		const dictionaries = await findDictionaries({
			...filters,
			projectIds: project.id
		}, skip, pageSize, sortOptions);
		if (!hasPermission(roles, "dictionary:read")({
			...res.locals,
			targetDictionaries: dictionaries
		})) {
			ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
			return;
		}
		const totalItems = await countDictionaries(filters);
		const responseData = formatPaginatedResponse({
			data: dictionaries.map((el) => mapDictionaryToAPI(el)),
			page,
			pageSize,
			totalPages: getNumberOfPages(totalItems),
			totalItems
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Retrieves a list of dictionaries keys based on filters and pagination.
*/
const getDictionariesKeys = async (_req, res, _next) => {
	const { project, roles } = res.locals;
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	try {
		const dictionaries = await findDictionaries({ projectIds: project.id });
		if (!hasPermission(roles, "dictionary:read")({
			...res.locals,
			targetDictionaries: dictionaries
		})) {
			ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
			return;
		}
		const responseData = formatResponse({ data: dictionaries.map((dictionary) => dictionary.key) });
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Retrieves a list of dictionaries keys based on filters and pagination.
*/
const getDictionariesUpdateTimestamp = async (_req, res, _next) => {
	const { project, roles } = res.locals;
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	try {
		const dictionaries = await findDictionaries({ projectIds: project.id });
		if (!hasPermission(roles, "dictionary:read")({
			...res.locals,
			targetDictionaries: dictionaries
		})) {
			ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
			return;
		}
		const responseData = formatResponse({ data: dictionaries.reduce((acc, dictionary) => ({
			...acc,
			[dictionary.id]: {
				key: dictionary.key,
				updatedAt: new Date(dictionary.updatedAt).getTime()
			}
		}), {}) });
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Retrieves a list of dictionaries based on filters and pagination.
*/
const getDictionaryByKey = async (req, res, _next) => {
	const { project, user, roles } = res.locals;
	const { dictionaryKey } = req.params;
	const version = req.query.version;
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	try {
		const dictionary = await getDictionaryByKey$1(dictionaryKey, project.id);
		if (!hasPermission(roles, "dictionary:read")({
			...res.locals,
			targetDictionaries: [dictionary]
		})) {
			ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
			return;
		}
		if (!dictionary.projectIds.map(String).includes(String(project.id))) {
			ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_PROJECT_MISMATCH");
			return;
		}
		const responseData = formatResponse({ data: mapDictionaryToAPI(dictionary, version) });
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Adds a new dictionary to the database.
*/
const addDictionary = async (req, res, _next) => {
	const { project, user, roles } = res.locals;
	const dictionaryData = req.body.dictionary;
	if (!dictionaryData) {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_DATA_NOT_FOUND");
		return;
	}
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	if (!dictionaryData.projectIds?.includes(String(project.id))) {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_PROJECT_MISMATCH");
		return;
	}
	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")(res.locals)) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	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
		});
		res.json(responseData);
		sendDictionaryUpdate([{
			dictionary: mapDictionaryToAPI(newDictionary),
			status: "ADDED"
		}]);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Check each dictionaries, add the new ones and update the existing ones.
* @param req - Express request object.
* @param res - Express response object.
* @returns Response containing the created dictionary.
*/
const pushDictionaries = async (req, res, _next) => {
	const { project, user, roles } = res.locals;
	const dictionaryData = req.body.dictionaries;
	if (typeof dictionaryData === "object" && Array.isArray(dictionaryData) && dictionaryData.length === 0) {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARIES_NOT_PROVIDED");
		return;
	} else if (!dictionaryData) {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_DATA_NOT_FOUND");
		return;
	}
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	if (!hasPermission(roles, "dictionary:write")(res.locals)) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	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"
		}))]);
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Updates an existing dictionary in the database.
*/
const updateDictionary = async (req, res, _next) => {
	const { dictionaryId } = req.params;
	const { project, roles } = res.locals;
	const dictionaryData = req.body;
	if (!dictionaryData) {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_DATA_NOT_FOUND");
		return;
	}
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	if (!dictionaryData.projectIds?.includes(String(project.id))) {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_PROJECT_MISMATCH");
		return;
	}
	if (typeof dictionaryId === "undefined") {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_ID_NOT_FOUND");
		return;
	}
	if (!hasPermission(roles, "dictionary:write")(res.locals)) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	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"
		}]);
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Deletes a dictionary from the database by its ID.
*/
const deleteDictionary = async (req, res, _next) => {
	const { project, roles } = res.locals;
	const { dictionaryId } = req.params;
	if (!dictionaryId) {
		ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_ID_NOT_FOUND");
		return;
	}
	if (!project) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECT_NOT_DEFINED");
		return;
	}
	if (!hasPermission(roles, "dictionary:admin")(res.locals)) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	try {
		if (!(await getDictionaryById(dictionaryId)).projectIds.includes(project.id)) {
			ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_PROJECT_MISMATCH");
			return;
		}
		const deletedDictionary = await deleteDictionaryById(dictionaryId);
		if (!deletedDictionary) {
			ErrorHandler.handleGenericErrorResponse(res, "DICTIONARY_NOT_FOUND", { dictionaryId });
			return;
		}
		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
		});
		res.json(responseData);
		sendDictionaryUpdate([{
			dictionary: apiResult,
			status: "DELETED"
		}]);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};

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