import { DictionaryModel } from "../models/dictionary.model.mjs";
import { ensureMongoDocumentToObject } from "../utils/ensureMongoDocumentToObject.mjs";
import { GenericError } from "../utils/errors/ErrorsClass.mjs";
import { removeObjectKeys } from "../utils/removeObjectKeys.mjs";
import { validateDictionary } from "../utils/validation/validateDictionary.mjs";
import { Types } from "mongoose";

//#region src/services/dictionary.service.ts
/**
* Finds dictionaries based on filters and pagination options.
* @param filters - MongoDB filter query.
* @param skip - Number of documents to skip.
* @param limit - Number of documents to limit.
* @returns List of dictionaries matching the filters.
*/
const findDictionaries = async (filters, skip = 0, limit = 100, sortOptions) => {
	try {
		return (await DictionaryModel.aggregate([
			{ $match: filters },
			...sortOptions && Object.keys(sortOptions).length > 0 ? [{ $sort: sortOptions }] : [],
			{ $skip: skip },
			{ $limit: limit }
		])).map((result) => new DictionaryModel(result));
	} catch (error) {
		console.error("Error fetching dictionaries:", error);
		throw error;
	}
};
/**
* Finds a dictionary by its ID.
* @param dictionaryId - The ID of the dictionary to find.
* @returns The dictionary matching the ID.
*/
/**
* Finds a dictionary by its ID and includes the 'versions' field.
* @param dictionaryId - The ID of the dictionary to find.
* @returns The dictionary matching the ID with available versions.
*/
const getDictionaryById = async (dictionaryId) => {
	const id = Types.ObjectId.isValid(dictionaryId) ? new Types.ObjectId(dictionaryId) : dictionaryId;
	const dictionaries = await DictionaryModel.aggregate([{ $match: { _id: id } }, { $addFields: { versions: { $map: {
		input: { $objectToArray: "$content" },
		as: "version",
		in: "$$version.k"
	} } } }]);
	if (!dictionaries.length) throw new GenericError("DICTIONARY_NOT_FOUND", { dictionaryId });
	return new DictionaryModel(dictionaries[0]);
};
/**
* Finds a dictionary by its ID.
* @param dictionaryKey - The ID of the dictionary to find.
* @returns The dictionary matching the ID.
*/
const getDictionaryByKey = async (dictionaryKey, projectId) => {
	return (await getDictionariesByKeys([dictionaryKey], projectId))[0];
};
const getDictionariesByKeys = async (dictionaryKeys, projectId) => {
	const dictionaries = await DictionaryModel.aggregate([{ $match: {
		key: { $in: dictionaryKeys },
		projectIds: projectId
	} }, { $addFields: { versions: { $map: {
		input: { $objectToArray: "$content" },
		as: "version",
		in: "$$version.k"
	} } } }]);
	if (!dictionaries) throw new GenericError("DICTIONARY_NOT_FOUND", {
		dictionaryKeys,
		projectId
	});
	return dictionaries.map((result) => new DictionaryModel(result));
};
const getDictionariesByTags = async (tags, projectId) => {
	return (await DictionaryModel.aggregate([{ $match: {
		tags: { $in: tags },
		projectIds: projectId
	} }, { $addFields: { versions: { $map: {
		input: { $objectToArray: "$content" },
		as: "version",
		in: "$$version.k"
	} } } }])).map((result) => new DictionaryModel(result));
};
/**
* Counts the total number of dictionaries that match the filters.
* @param filters - MongoDB filter query.
* @returns Total number of dictionaries.
*/
const countDictionaries = async (filters) => {
	const result = await DictionaryModel.countDocuments(filters);
	if (typeof result === "undefined") throw new GenericError("DICTIONARY_COUNT_FAILED", { filters });
	return result;
};
/**
* Creates a new dictionary in the database.
* @param  dictionary - The dictionary data to create.
* @returns The created dictionary.
*/
const createDictionary = async (dictionary) => {
	const errors = await validateDictionary(dictionary);
	if (Object.keys(errors).length > 0) throw new GenericError("DICTIONARY_INVALID_FIELDS", { errors });
	return await DictionaryModel.create(dictionary);
};
/**
* Updates an existing dictionary in the database by its ID.
* @param dictionaryId - The ID of the dictionary to update.
* @param dictionary - The updated dictionary data.
* @returns The updated dictionary.
*/
const updateDictionaryById = async (dictionaryId, dictionary) => {
	const dictionaryToUpdate = removeObjectKeys(ensureMongoDocumentToObject(dictionary), ["id"]);
	const errors = await validateDictionary(dictionaryToUpdate, Object.keys(dictionaryToUpdate));
	if (Object.keys(errors).length > 0) throw new GenericError("DICTIONARY_INVALID_FIELDS", {
		dictionaryId,
		errors
	});
	if ((await DictionaryModel.updateOne({ _id: dictionaryId }, dictionaryToUpdate)).matchedCount === 0) throw new GenericError("DICTIONARY_UPDATE_FAILED", { dictionaryId });
	return await getDictionaryById(dictionaryId);
};
/**
* Updates an existing dictionary in the database by its key.
* @param dictionaryKey - The ID of the dictionary to update.
* @param dictionary - The updated dictionary data.
* @returns The updated dictionary.
*/
const updateDictionaryByKey = async (dictionaryKey, dictionary, projectId) => {
	const existing = await DictionaryModel.findOne({
		key: dictionaryKey,
		projectIds: projectId
	});
	if (!existing) throw new GenericError("DICTIONARY_UPDATE_FAILED", { dictionaryKey });
	const dictionaryToUpdate = removeObjectKeys(ensureMongoDocumentToObject(dictionary), ["id"]);
	Object.assign(existing, dictionaryToUpdate);
	await existing.save();
	return existing;
};
/**
* Deletes a dictionary from the database by its ID.
* @param dictionaryId - The ID of the dictionary to delete.
* @returns The result of the deletion operation.
*/
const deleteDictionaryById = async (dictionaryId) => {
	const dictionary = await DictionaryModel.findByIdAndDelete(dictionaryId);
	if (!dictionary) throw new GenericError("DICTIONARY_NOT_FOUND", { dictionaryId });
	return dictionary;
};
const getVersionNumber = (version) => {
	const match = version.match(/^v(\d+)$/);
	if (!match) throw new Error(`Invalid version format: ${version}`);
	return parseInt(match[1], 10);
};
const incrementVersion = (dictionary) => {
	const VERSION_PREFIX = "v";
	const versions = [...dictionary.content.keys() ?? []];
	const lastVersion = versions[versions.length - 1];
	let newNumber = getVersionNumber(lastVersion) + 1;
	let newVersion = `${VERSION_PREFIX}${newNumber}`;
	while (versions.includes(newVersion)) {
		newNumber += 1;
		newVersion = `${VERSION_PREFIX}${newNumber}`;
	}
	return newVersion;
};

//#endregion
export { countDictionaries, createDictionary, deleteDictionaryById, findDictionaries, getDictionariesByKeys, getDictionariesByTags, getDictionaryById, getDictionaryByKey, incrementVersion, updateDictionaryById, updateDictionaryByKey };
//# sourceMappingURL=dictionary.service.mjs.map