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, getDictionariesByKeys, getDictionaryById, getDictionaryByKey as getDictionaryByKey$1, getExistingDictionaryKey, 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";

//#region src/controllers/dictionary.controller.ts
/**
* 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.key]: 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: 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;
	const dictionariesKeys = dictionaryData.map((dictionary) => dictionary.key);
	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 { existingDictionariesKey, newDictionariesKey } = await getExistingDictionaryKey(dictionariesKeys, project.id);
		const existingDictionaries = dictionaryData.filter((dictionary) => existingDictionariesKey.includes(dictionary.key));
		const newDictionaries = dictionaryData.filter((dictionary) => newDictionariesKey.includes(dictionary.key));
		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: dictionaryDataEl.content ?? {} }]]),
				key: dictionaryDataEl.key
			};
			try {
				const newDictionary = await createDictionary(dictionary);
				newDictionariesResult.push(mapDictionaryToAPI(newDictionary));
			} catch (error) {
				ErrorHandler.handleAppErrorResponse(res, error);
				return;
			}
		}
		if (existingDictionariesKey.length >= 0) {
			const existingDictionariesDB = await getDictionariesByKeys(existingDictionariesKey, project.id);
			for (const dictionaryDataEl of existingDictionaries) {
				const existingDictionaryDB = existingDictionariesDB.find((dictionaryDB) => dictionaryDB.key === dictionaryDataEl.key);
				const versionList = [...existingDictionaryDB.content.keys() ?? []];
				const lastVersion = versionList[versionList.length - 1];
				const lastContent = existingDictionaryDB.content.get(lastVersion)?.content ?? null;
				const isSameContent = JSON.stringify(lastContent) === JSON.stringify(dictionaryDataEl.content);
				let newContent = existingDictionaryDB.content;
				if (!isSameContent) {
					const newContentVersion = incrementVersion(existingDictionaryDB);
					existingDictionaryDB.content.set(newContentVersion, { content: dictionaryDataEl.content ?? {} });
					newContent = existingDictionaryDB.content;
				}
				const dictionary = {
					...ensureMongoDocumentToObject(existingDictionaryDB),
					...dictionaryDataEl,
					content: newContent,
					projectIds: [String(project.id)],
					creatorId: user.id,
					key: dictionaryDataEl.key
				};
				try {
					const updatedDictionary = await updateDictionaryByKey(dictionaryDataEl.key, dictionary, project.id);
					updatedDictionariesResult.push(mapDictionaryToAPI(updatedDictionary));
				} catch (error) {
					ErrorHandler.handleAppErrorResponse(res, error);
					return;
				}
			}
		}
		const result = {
			newDictionaries: newDictionariesResult.map((dictionary) => dictionary.key),
			updatedDictionaries: updatedDictionariesResult.map((dictionary) => dictionary.key),
			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