import { logger } from "../logger/index.mjs";
import { formatPaginatedResponse, formatResponse } from "../utils/responseData.mjs";
import { ErrorHandler } from "../utils/errors/ErrorHandler.mjs";
import { countProjects, createProject, deleteProjectById, findProjects, getProjectById, updateProjectById } from "../services/project.service.mjs";
import { triggerAll, triggerSingleWebhook } from "../services/webhook.service.mjs";
import { hasPermission } from "../utils/permissions.mjs";
import { getUsersByIds } from "../services/user.service.mjs";
import { SessionModel } from "../models/session.model.mjs";
import { getPlanDetails } from "../utils/plan.mjs";
import { getCIStatus, installCI } from "../services/ci.service.mjs";
import { getProjectFiltersAndPagination } from "../utils/filtersAndPagination/getProjectFiltersAndPagination.mjs";
import { mapProjectToAPI, mapProjectsToAPI } from "../utils/mapper/project.mjs";
import { t } from "fastify-intlayer";

//#region src/controllers/project.controller.ts
/**
* Retrieves a list of projects based on filters and pagination.
*/
const getProjects = async (request, reply) => {
	const { user, organization, roles } = request.locals || {};
	const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } = getProjectFiltersAndPagination(request);
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!organization && !roles?.includes("admin")) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	try {
		const projects = await findProjects(filters, skip, pageSize, sortOptions);
		if (!hasPermission(roles || [], "project:read")({
			...request.locals,
			targetProjects: projects
		})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
		const totalItems = await countProjects(filters);
		const responseData = formatPaginatedResponse({
			data: mapProjectsToAPI(projects),
			page,
			pageSize,
			totalPages: getNumberOfPages(totalItems),
			totalItems
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Adds a new project to the database.
*/
const addProject = async (request, reply) => {
	const { organization, user, roles } = request.locals || {};
	const projectData = request.body;
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if (!projectData) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_DATA_NOT_FOUND");
	if (!hasPermission(roles || [], "organization:admin")({
		...request.locals,
		targetOrganizations: [organization]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	const { plan } = organization;
	const planType = getPlanDetails(plan);
	if (planType.numberOfProjects) {
		if (await countProjects({ organizationId: organization.id }) >= planType.numberOfProjects) return ErrorHandler.handleGenericErrorResponse(reply, "PLAN_PROJECT_LIMIT_REACHED", { organizationId: organization.id });
	}
	const project = {
		membersIds: [user.id],
		adminsIds: [user.id],
		creatorId: user.id,
		organizationId: organization.id,
		...projectData
	};
	try {
		const formattedProject = mapProjectToAPI(await createProject(project));
		const responseData = formatResponse({
			message: t({
				en: "Project created successfully",
				fr: "Projet créé avec succès",
				es: "Proyecto creado con éxito"
			}),
			description: t({
				en: "Your project has been created successfully",
				fr: "Votre projet a été créé avec succès",
				es: "Su proyecto ha sido creado con éxito"
			}),
			data: formattedProject
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Updates an existing project in the database.
*/
const updateProject = async (request, reply) => {
	const { organization, project, user, roles } = request.locals || {};
	const projectData = request.body;
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_DATA_NOT_FOUND");
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if (String(project.organizationId) !== String(organization.id)) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_IN_ORGANIZATION");
	if (!hasPermission(roles || [], "project:write")({
		...request.locals,
		targetProjectIds: [String(project.id)]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const formattedProject = mapProjectToAPI(await updateProjectById(project.id, projectData));
		const responseData = formatResponse({
			message: t({
				en: "Project updated successfully",
				fr: "Projet mis à jour avec succès",
				es: "Proyecto actualizado con éxito"
			}),
			description: t({
				en: "Your project has been updated successfully",
				fr: "Votre projet a été mis à jour avec succès",
				es: "Su proyecto ha sido actualizado con éxito"
			}),
			data: formattedProject
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Update members to the dictionary in the database.
*/
const updateProjectMembers = async (request, reply) => {
	const { user, project, organization, roles } = request.locals || {};
	const { membersIds } = request.body;
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if (membersIds?.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_MUST_HAVE_MEMBER");
	if (membersIds?.map((el) => el.isAdmin)?.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_MUST_HAVE_ADMIN");
	if (!hasPermission(roles || [], "project:write")({
		...request.locals,
		targetProjectIds: [String(project.id)]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const existingUsers = [];
		if (membersIds) {
			const userIdList = membersIds?.filter((member) => !organization?.membersIds.includes(member.userId)).map((member) => member.userId);
			const users = await getUsersByIds(userIdList);
			if (users) {
				const userMap = users.map((user) => ({
					user,
					isAdmin: membersIds.find((member) => String(member.userId) === String(user.id))?.isAdmin ?? false
				}));
				existingUsers.push(...userMap);
			}
		}
		const formattedMembers = existingUsers.map((user) => user.user.id);
		const formattedAdmin = existingUsers.filter((el) => el.isAdmin).map((user) => user.user.id);
		const formattedProject = mapProjectToAPI(await updateProjectById(project.id, {
			...project,
			membersIds: formattedMembers,
			adminsIds: formattedAdmin
		}));
		const responseData = formatResponse({
			message: t({
				en: "Project members updated successfully",
				fr: "Membres du projet mis à jour avec succès",
				es: "Miembros del proyecto actualizados con éxito"
			}),
			description: t({
				en: "Your project members have been updated successfully",
				fr: "Les membres de votre projet ont été mis à jour avec succès",
				es: "Los miembros de su proyecto han sido actualizados con éxito"
			}),
			data: formattedProject
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Pushes a project configuration to the database.
*/
const pushProjectConfiguration = async (request, reply) => {
	const { user, project, roles } = request.locals || {};
	const projectConfiguration = request.body;
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!hasPermission(roles || [], "project:write")({
		...request.locals,
		targetProjectIds: [String(project.id)]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const projectObject = await getProjectById(project.id);
		if (projectConfiguration.ai && projectObject.configuration?.ai?.apiKey) projectConfiguration.ai.apiKey = projectObject.configuration.ai.apiKey;
		projectObject.configuration = projectConfiguration;
		await projectObject.save();
		if (!projectObject.configuration) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_UPDATE_FAILED", { projectId: project.id });
		const responseData = formatResponse({
			message: t({
				en: "Project configuration updated successfully",
				fr: "Configuration du projet mise à jour avec succès",
				es: "Configuración del proyecto actualizada con éxito"
			}),
			description: t({
				en: "Your project configuration has been updated successfully",
				fr: "La configuration du projet a été mise à jour avec succès",
				es: "Su configuración del proyecto ha sido actualizada con éxito"
			}),
			data: mapProjectToAPI(projectObject)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Triggers CI builds for a project (Git provider pipelines and webhooks)
*/
const triggerBuild = async (request, reply) => {
	const { project, roles } = request.locals || {};
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!hasPermission(roles || [], "project:write")({
		...request.locals,
		targetProjectIds: [String(project.id)]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const fullProject = await getProjectById(project.id);
		const results = await triggerAll(fullProject);
		const responseData = formatResponse({
			message: t({
				en: "Build triggers initiated",
				fr: "Déclenchement des builds initié",
				es: "Inicio de los triggers de build"
			}),
			description: t({
				en: "CI pipelines and webhooks have been triggered",
				fr: "Les pipelines CI et webhooks ont été déclenchés",
				es: "Los pipelines CI y webhooks han sido activados"
			}),
			data: { results }
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Triggers a single webhook by index
*/
const triggerWebhook = async (request, reply) => {
	const { project, roles } = request.locals || {};
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!hasPermission(roles || [], "project:write")({
		...request.locals,
		targetProjectIds: [String(project.id)]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const { webhookIndex } = request.body;
		if (typeof webhookIndex !== "number" || webhookIndex < 0) return ErrorHandler.handleGenericErrorResponse(reply, "INVALID_REQUEST_BODY");
		const fullProject = await getProjectById(project.id);
		const result = await triggerSingleWebhook(fullProject, webhookIndex);
		const responseData = formatResponse({
			message: t({
				en: "Webhook triggered",
				fr: "Webhook déclenché",
				es: "Webhook activado"
			}),
			description: t({
				en: `Webhook "${result.target}" has been triggered`,
				fr: `Le webhook "${result.target}" a été déclenché`,
				es: `El webhook "${result.target}" ha sido activado`
			}),
			data: result
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Deletes a project from the database by its ID.
*/
const deleteProject = async (_request, reply) => {
	const { user, organization, project, session, roles } = _request.locals || {};
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
	if (!hasPermission(roles || [], "project:admin")({
		..._request.locals,
		targetProjectIds: [String(project.id)]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		const projectToDelete = await getProjectById(project.id);
		if (String(projectToDelete.organizationId) !== String(organization.id)) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_IN_ORGANIZATION");
		const deletedProject = await deleteProjectById(project.id);
		if (!deletedProject) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED", { projectId: project.id });
		logger.info(`Project deleted: ${String(deletedProject.id)}`);
		const responseData = formatResponse({
			message: t({
				en: "Project deleted successfully",
				fr: "Projet supprimé avec succès",
				es: "Proyecto eliminado con éxito"
			}),
			description: t({
				en: "Your project has been deleted successfully",
				fr: "Votre projet a été supprimé avec succès",
				es: "Su proyecto ha sido eliminado con éxito"
			}),
			data: mapProjectToAPI(deletedProject)
		});
		await SessionModel.updateOne({ _id: session.id }, { $set: { activeProjectId: null } });
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Select a project.
*/
const selectProject = async (request, reply) => {
	const { projectId } = request.params;
	const { session } = request.locals || {};
	if (!projectId) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_ID_NOT_FOUND");
	if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
	try {
		const project = await getProjectById(projectId);
		await SessionModel.updateOne({ _id: session.id }, { $set: { activeProjectId: String(projectId) } });
		const responseData = formatResponse({
			message: t({
				en: "Project selected successfully",
				fr: "Projet sélectionné avec succès",
				es: "Proyecto seleccionado con éxito"
			}),
			description: t({
				en: "Your project has been selected successfully",
				fr: "Votre projet a été sélectionné avec succès",
				es: "Su proyecto ha sido seleccionado con éxito"
			}),
			data: mapProjectToAPI(project)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Unselect a project.
*/
const unselectProject = async (_request, reply) => {
	const { session } = _request.locals || {};
	if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
	try {
		await SessionModel.updateOne({ _id: session.id }, { $set: { activeProjectId: null } });
		const responseData = formatResponse({
			message: t({
				en: "Project unselected successfully",
				fr: "Projet désélectionné avec succès",
				es: "Proyecto deseleccionado con éxito"
			}),
			description: t({
				en: "Your project has been unselected successfully",
				fr: "Votre projet a été désélectionné avec succès",
				es: "Su proyecto ha sido deseleccionado con éxito"
			}),
			data: null
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Get CI configuration status for the current project
*/
const getCIConfiguration = async (request, reply) => {
	const { project, user } = request.locals || {};
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	try {
		const responseData = formatResponse({ data: await getCIStatus(project) });
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Push CI configuration file to the repository
*/
const pushCIConfiguration = async (request, reply) => {
	const { project, user } = request.locals || {};
	if (!project) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECT_NOT_DEFINED");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	try {
		await installCI(project);
		const responseData = formatResponse({
			message: t({
				en: "CI configuration installed successfully",
				fr: "Configuration CI installée avec succès",
				es: "Configuración CI instalada con éxito"
			}),
			description: t({
				en: "The CI workflow file has been added to your repository",
				fr: "Le fichier de workflow CI a été ajouté à votre dépôt",
				es: "El archivo de flujo de trabajo CI se ha agregado a su repositorio"
			}),
			data: { success: true }
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};

//#endregion
export { addProject, deleteProject, getCIConfiguration, getProjects, pushCIConfiguration, pushProjectConfiguration, selectProject, triggerBuild, triggerWebhook, unselectProject, updateProject, updateProjectMembers };
//# sourceMappingURL=project.controller.mjs.map