import { logger } from "../logger/index.mjs";
import { formatPaginatedResponse, formatResponse } from "../utils/responseData.mjs";
import { ErrorHandler } from "../utils/errors/ErrorHandler.mjs";
import { countOrganizations, createOrganization, deleteOrganizationById, findOrganizations, getOrganizationById, updateOrganizationById } from "../services/organization.service.mjs";
import { findProjects } from "../services/project.service.mjs";
import { hasPermission } from "../utils/permissions.mjs";
import { createUser, getUserByEmail, getUsersByIds } from "../services/user.service.mjs";
import { SessionModel } from "../models/session.model.mjs";
import { sendEmail } from "../services/email.service.mjs";
import { getOrganizationFiltersAndPagination } from "../utils/filtersAndPagination/getOrganizationFiltersAndPagination.mjs";
import { mapOrganizationToAPI, mapOrganizationsToAPI } from "../utils/mapper/organization.mjs";
import { getPlanDetails } from "../utils/plan.mjs";
import { t } from "express-intlayer";
import { Stripe } from "stripe";

//#region src/controllers/organization.controller.ts
/**
* Retrieves a list of organizations based on filters and pagination.
*/
const getOrganizations = async (req, res, _next) => {
	const { user, roles } = res.locals;
	const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } = getOrganizationFiltersAndPagination(req, res);
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	try {
		const organizations = await findOrganizations(filters, skip, pageSize, sortOptions);
		if (!hasPermission(roles, "organization:read")({
			...res.locals,
			targetOrganizations: organizations
		})) {
			ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
			return;
		}
		const totalItems = await countOrganizations(filters);
		const responseData = formatPaginatedResponse({
			data: mapOrganizationsToAPI(organizations),
			page,
			pageSize,
			totalPages: getNumberOfPages(totalItems),
			totalItems
		});
		res.status(200).json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Retrieves an organization by its ID.
*/
const getOrganization = async (req, res, _next) => {
	const { roles } = res.locals;
	const { organizationId } = req.params;
	if (!organizationId) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_ID_NOT_FOUND");
		return;
	}
	try {
		const organization = await getOrganizationById(organizationId);
		if (!hasPermission(roles, "organization:read")({
			...res.locals,
			targetOrganizations: [organization]
		})) {
			ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
			return;
		}
		const responseData = formatResponse({ data: mapOrganizationToAPI(organization) });
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Adds a new organization to the database.
*/
const addOrganization = async (req, res, _next) => {
	const { user } = res.locals;
	const organization = req.body;
	if (!organization) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_DATA_NOT_FOUND");
		return;
	}
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	try {
		const newOrganization = await createOrganization(organization, user.id);
		const responseData = formatResponse({
			message: t({
				en: "Organization created successfully",
				fr: "Organisation créée avec succès",
				es: "Organización creada con éxito"
			}),
			description: t({
				en: "Your organization has been created successfully",
				fr: "Votre organisation a été créée avec succès",
				es: "Su organización ha sido creada con éxito"
			}),
			data: mapOrganizationToAPI(newOrganization)
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Updates an existing organization in the database.
*/
const updateOrganization = async (req, res, _next) => {
	const { organization, roles } = res.locals;
	const organizationFields = req.body;
	if (!organizationFields) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_DATA_NOT_FOUND");
		return;
	}
	if (!organization) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
		return;
	}
	if (!hasPermission(roles, "organization:write")({
		...res.locals,
		targetOrganizations: [organization]
	})) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	try {
		const updatedOrganization = await updateOrganizationById(organization.id, organizationFields);
		const responseData = formatResponse({
			message: t({
				en: "Organization updated successfully",
				fr: "Organisation mise à jour avec succès",
				es: "Organización actualizada con éxito"
			}),
			description: t({
				en: "Your organization has been updated successfully",
				fr: "Votre organisation a été mise à jour avec succès",
				es: "Su organización ha sido actualizada con éxito"
			}),
			data: mapOrganizationToAPI(updatedOrganization)
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Add member to the organization in the database.
*/
const addOrganizationMember = async (req, res, _next) => {
	const { organization, user, roles } = res.locals;
	const { userEmail } = req.body;
	if (!organization) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
		return;
	}
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	if (!hasPermission(roles, "organization:admin")({
		...res.locals,
		targetOrganizations: [organization]
	})) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	const planType = getPlanDetails(organization.plan);
	if (planType.numberOfOrganizationUsers && organization.membersIds.length >= planType.numberOfOrganizationUsers) {
		ErrorHandler.handleGenericErrorResponse(res, "PLAN_USER_LIMIT_REACHED", { organizationId: organization.id });
		return;
	}
	try {
		let newMember = await getUserByEmail(userEmail);
		if (!newMember) {
			const newUser = await createUser({ email: userEmail });
			if (!newUser) {
				ErrorHandler.handleGenericErrorResponse(res, "USER_CREATION_FAILED", { email: userEmail });
				return;
			}
			newMember = newUser;
		}
		const updatedOrganization = await updateOrganizationById(organization.id, {
			...organization,
			membersIds: [...organization.membersIds, newMember.id]
		});
		const responseData = formatResponse({
			message: t({
				en: "Organization updated successfully",
				fr: "Organisation mise à jour avec succès",
				es: "Organización actualizada con éxito"
			}),
			description: t({
				en: "Your organization has been updated successfully",
				fr: "Votre organisation a été mise à jour avec succès",
				es: "Su organización ha sido actualizada con éxito"
			}),
			data: mapOrganizationToAPI(updatedOrganization)
		});
		res.json(responseData);
		await sendEmail({
			type: "invite",
			to: userEmail,
			username: newMember.email.slice(0, newMember.email.indexOf("@")),
			invitedByUsername: user.name,
			invitedByEmail: user.email,
			organizationName: organization.name,
			inviteLink: `${process.env.CLIENT_URL}/auth/login?email=${newMember.email}`,
			inviteFromIp: req.ip ?? "",
			inviteFromLocation: req.hostname
		});
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Update members to the organization in the database.
*/
const updateOrganizationMembers = async (req, res, _next) => {
	const { organization, roles } = res.locals;
	const { membersIds, adminsIds } = req.body;
	if (!organization) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
		return;
	}
	if (!hasPermission(roles, "organization:admin")({
		...res.locals,
		targetOrganizations: [organization]
	})) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	if (!membersIds) {
		ErrorHandler.handleGenericErrorResponse(res, "INVALID_REQUEST_BODY");
		return;
	}
	if (membersIds?.length === 0) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_MUST_HAVE_MEMBER");
		return;
	}
	if (adminsIds?.filter((id) => membersIds?.includes(id)).length === 0) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_MUST_HAVE_ADMIN");
		return;
	}
	try {
		const existingUsers = await getUsersByIds(membersIds);
		if (!existingUsers) {
			ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
			return;
		}
		const existingAdmins = await getUsersByIds(adminsIds);
		if (!existingAdmins) {
			ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
			return;
		}
		const updatedOrganization = await updateOrganizationById(organization.id, {
			membersIds: existingUsers.map((user) => user.id),
			adminsIds: existingAdmins.map((user) => user.id)
		});
		const responseData = formatResponse({
			message: t({
				en: "Organization updated successfully",
				fr: "Organisation mise à jour avec succès",
				es: "Organización actualizada con éxito"
			}),
			description: t({
				en: "Your organization has been updated successfully",
				fr: "Votre organisation a été mise à jour avec succès",
				es: "Su organización ha sido actualizada con éxito"
			}),
			data: mapOrganizationToAPI(updatedOrganization)
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Admin-only: Update members of any organization by ID
*/
const updateOrganizationMembersById = async (req, res, _next) => {
	const { user } = res.locals;
	const { organizationId } = req.params;
	const { membersIds, adminsIds } = req.body;
	if (!user) {
		ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_DEFINED");
		return;
	}
	if (user.role !== "admin") {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	if (!membersIds) {
		ErrorHandler.handleGenericErrorResponse(res, "INVALID_REQUEST_BODY");
		return;
	}
	if (membersIds?.length === 0) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_MUST_HAVE_MEMBER");
		return;
	}
	try {
		const targetOrganization = await getOrganizationById(organizationId);
		if (!targetOrganization) {
			ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_FOUND");
			return;
		}
		const existingUsers = await getUsersByIds(membersIds);
		if (!existingUsers) {
			ErrorHandler.handleGenericErrorResponse(res, "USER_NOT_FOUND");
			return;
		}
		const finalAdminsIds = adminsIds && adminsIds.length > 0 ? adminsIds : targetOrganization.adminsIds;
		const existingAdmins = finalAdminsIds ? await getUsersByIds(finalAdminsIds) : [];
		if (!existingAdmins || existingAdmins.length === 0) {
			ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_MUST_HAVE_ADMIN");
			return;
		}
		const updatedOrganization = await updateOrganizationById(targetOrganization.id, {
			membersIds: existingUsers.map((user$1) => user$1.id),
			adminsIds: existingAdmins.map((user$1) => user$1.id)
		});
		const responseData = formatResponse({
			message: t({
				en: "Organization members updated successfully",
				fr: "Membres de l'organisation mis à jour avec succès",
				es: "Miembros de la organización actualizados con éxito"
			}),
			description: t({
				en: "Organization members have been updated successfully",
				fr: "Les membres de l'organisation ont été mis à jour avec succès",
				es: "Los miembros de la organización han sido actualizados con éxito"
			}),
			data: mapOrganizationToAPI(updatedOrganization)
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Deletes an organization from the database by its ID.
*/
const deleteOrganization = async (_req, res, _next) => {
	const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
	const { organization, roles } = res.locals;
	if (!organization) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_DEFINED");
		return;
	}
	if ((await findProjects({ organizationId: organization.id })).length > 0) {
		ErrorHandler.handleGenericErrorResponse(res, "PROJECTS_EXIST", { organizationId: organization.id });
		return;
	}
	if (!hasPermission(roles, "organization:admin")({
		...res.locals,
		targetOrganizations: [organization]
	})) {
		ErrorHandler.handleGenericErrorResponse(res, "PERMISSION_DENIED");
		return;
	}
	try {
		if (organization.plan?.subscriptionId) await stripe.subscriptions.cancel(organization.plan.subscriptionId);
		const deletedOrganization = await deleteOrganizationById(organization.id);
		if (!deletedOrganization) {
			ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_NOT_FOUND", { organizationId: organization.id });
			return;
		}
		logger.info(`Organization deleted: ${String(deletedOrganization.id)}`);
		const responseData = formatResponse({
			message: t({
				en: "Organization deleted successfully",
				fr: "Organisation supprimée avec succès",
				es: "Organización eliminada con éxito"
			}),
			description: t({
				en: "Your organization has been deleted successfully",
				fr: "Votre organisation a été supprimée avec succès",
				es: "Su organización ha sido eliminada con éxito"
			}),
			data: mapOrganizationToAPI(deletedOrganization)
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Select an organization.
*/
const selectOrganization = async (req, res, _next) => {
	const { organizationId } = req.params;
	const { session } = res.locals;
	if (!organizationId) {
		ErrorHandler.handleGenericErrorResponse(res, "ORGANIZATION_ID_NOT_FOUND");
		return;
	}
	if (typeof session === "undefined") {
		ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
		return;
	}
	try {
		const organization = await getOrganizationById(organizationId);
		await SessionModel.updateOne({ _id: session.id }, { $set: {
			activeOrganizationId: String(organization.id),
			activeProjectId: null
		} });
		const responseData = formatResponse({
			message: t({
				en: "Organization retrieved successfully",
				fr: "Organisation récupérée avec succès",
				es: "Organización recuperada con éxito"
			}),
			description: t({
				en: "Your organization has been retrieved successfully",
				fr: "Votre organisation a été récupérée avec succès",
				es: "Su organización ha sido recuperada con éxito"
			}),
			data: mapOrganizationToAPI(organization)
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};
/**
* Unselect an organization.
*/
const unselectOrganization = async (_req, res, _next) => {
	const { session } = res.locals;
	try {
		if (typeof session === "undefined") {
			ErrorHandler.handleGenericErrorResponse(res, "SESSION_NOT_DEFINED");
			return;
		}
		await SessionModel.updateOne({ _id: session.id }, { $set: {
			activeOrganizationId: null,
			activeProjectId: null
		} });
		const responseData = formatResponse({
			message: t({
				en: "Organization unselected successfully",
				fr: "Organisation désélectionnée avec succès",
				es: "Organización deseleccionada con éxito"
			}),
			description: t({
				en: "Your organization has been unselected successfully",
				fr: "Votre organisation a été désélectionnée avec succès",
				es: "Su organización ha sido deseleccionada con éxito"
			}),
			data: null
		});
		res.json(responseData);
		return;
	} catch (error) {
		ErrorHandler.handleAppErrorResponse(res, error);
		return;
	}
};

//#endregion
export { addOrganization, addOrganizationMember, deleteOrganization, getOrganization, getOrganizations, selectOrganization, unselectOrganization, updateOrganization, updateOrganizationMembers, updateOrganizationMembersById };
//# sourceMappingURL=organization.controller.mjs.map