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 "fastify-intlayer";
import { Stripe } from "stripe";

//#region src/controllers/organization.controller.ts
/**
* Retrieves a list of organizations based on filters and pagination.
*/
const getOrganizations = async (request, reply) => {
	const { user, roles } = request.locals || {};
	const { filters, sortOptions, pageSize, skip, page, getNumberOfPages } = getOrganizationFiltersAndPagination(request);
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	try {
		const organizations = await findOrganizations(filters, skip, pageSize, sortOptions);
		if (!hasPermission(roles || [], "organization:read")({
			...request.locals,
			targetOrganizations: organizations
		})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
		const totalItems = await countOrganizations(filters);
		const responseData = formatPaginatedResponse({
			data: mapOrganizationsToAPI(organizations),
			page,
			pageSize,
			totalPages: getNumberOfPages(totalItems),
			totalItems
		});
		return reply.code(200).send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Retrieves an organization by its ID.
*/
const getOrganization = async (request, reply) => {
	const { roles } = request.locals || {};
	const { organizationId } = request.params;
	if (!organizationId) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_ID_NOT_FOUND");
	try {
		const organization = await getOrganizationById(organizationId);
		if (!hasPermission(roles || [], "organization:read")({
			...request.locals,
			targetOrganizations: [organization]
		})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
		const responseData = formatResponse({ data: mapOrganizationToAPI(organization) });
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Adds a new organization to the database.
*/
const addOrganization = async (request, reply) => {
	const { user } = request.locals || {};
	const organization = request.body;
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_DATA_NOT_FOUND");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	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)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Updates an existing organization in the database.
*/
const updateOrganization = async (request, reply) => {
	const { organization, roles } = request.locals || {};
	const organizationFields = request.body;
	if (!organizationFields) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_DATA_NOT_FOUND");
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if (!hasPermission(roles || [], "organization:write")({
		...request.locals,
		targetOrganizations: [organization]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	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)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Add member to the organization in the database.
*/
const addOrganizationMember = async (request, reply) => {
	const { organization, user, roles } = request.locals || {};
	const { userEmail } = request.body;
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (!hasPermission(roles || [], "organization:admin")({
		...request.locals,
		targetOrganizations: [organization]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	const planType = getPlanDetails(organization.plan);
	if (planType.numberOfOrganizationUsers && organization.membersIds.length >= planType.numberOfOrganizationUsers) return ErrorHandler.handleGenericErrorResponse(reply, "PLAN_USER_LIMIT_REACHED", { organizationId: organization.id });
	try {
		let newMember = await getUserByEmail(userEmail);
		if (!newMember) {
			const newUser = await createUser({ email: userEmail });
			if (!newUser) return ErrorHandler.handleGenericErrorResponse(reply, "USER_CREATION_FAILED", { email: userEmail });
			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)
		});
		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.APP_URL}/auth/login?email=${newMember.email}`,
			inviteFromIp: request.ip ?? "",
			inviteFromLocation: request.hostname
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Update members to the organization in the database.
*/
const updateOrganizationMembers = async (request, reply) => {
	const { organization, roles } = request.locals || {};
	const { membersIds, adminsIds } = request.body;
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if (!hasPermission(roles || [], "organization:admin")({
		...request.locals,
		targetOrganizations: [organization]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	if (!membersIds) return ErrorHandler.handleGenericErrorResponse(reply, "INVALID_REQUEST_BODY");
	if (membersIds?.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_MUST_HAVE_MEMBER");
	if (adminsIds?.filter((id) => membersIds?.includes(id)).length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_MUST_HAVE_ADMIN");
	try {
		const existingUsers = await getUsersByIds(membersIds);
		if (!existingUsers) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_FOUND");
		const existingAdmins = await getUsersByIds(adminsIds);
		if (!existingAdmins) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_FOUND");
		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)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Admin-only: Update members of any organization by ID
*/
const updateOrganizationMembersById = async (request, reply) => {
	const { user } = request.locals || {};
	const { organizationId } = request.params;
	const { membersIds, adminsIds } = request.body;
	if (!user) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_DEFINED");
	if (user.role !== "admin") return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	if (!membersIds) return ErrorHandler.handleGenericErrorResponse(reply, "INVALID_REQUEST_BODY");
	if (membersIds?.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_MUST_HAVE_MEMBER");
	try {
		const targetOrganization = await getOrganizationById(organizationId);
		if (!targetOrganization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_FOUND");
		const existingUsers = await getUsersByIds(membersIds);
		if (!existingUsers) return ErrorHandler.handleGenericErrorResponse(reply, "USER_NOT_FOUND");
		const finalAdminsIds = adminsIds && adminsIds.length > 0 ? adminsIds : targetOrganization.adminsIds;
		const existingAdmins = finalAdminsIds ? await getUsersByIds(finalAdminsIds) : [];
		if (!existingAdmins || existingAdmins.length === 0) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_MUST_HAVE_ADMIN");
		const updatedOrganization = await updateOrganizationById(targetOrganization.id, {
			membersIds: existingUsers.map((user) => user.id),
			adminsIds: existingAdmins.map((user) => user.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)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Deletes an organization from the database by its ID.
*/
const deleteOrganization = async (_request, reply) => {
	const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
	const { organization, roles } = _request.locals || {};
	if (!organization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_DEFINED");
	if ((await findProjects({ organizationId: organization.id })).length > 0) return ErrorHandler.handleGenericErrorResponse(reply, "PROJECTS_EXIST", { organizationId: organization.id });
	if (!hasPermission(roles || [], "organization:admin")({
		..._request.locals,
		targetOrganizations: [organization]
	})) return ErrorHandler.handleGenericErrorResponse(reply, "PERMISSION_DENIED");
	try {
		if (organization.plan?.subscriptionId) await stripe.subscriptions.cancel(organization.plan.subscriptionId);
		const deletedOrganization = await deleteOrganizationById(organization.id);
		if (!deletedOrganization) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_NOT_FOUND", { organizationId: organization.id });
		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)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Select an organization.
*/
const selectOrganization = async (request, reply) => {
	const { organizationId } = request.params;
	const { session } = request.locals || {};
	if (!organizationId) return ErrorHandler.handleGenericErrorResponse(reply, "ORGANIZATION_ID_NOT_FOUND");
	if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
	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)
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};
/**
* Unselect an organization.
*/
const unselectOrganization = async (_request, reply) => {
	const { session } = _request.locals || {};
	try {
		if (typeof session === "undefined") return ErrorHandler.handleGenericErrorResponse(reply, "SESSION_NOT_DEFINED");
		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
		});
		return reply.send(responseData);
	} catch (error) {
		return ErrorHandler.handleAppErrorResponse(reply, error);
	}
};

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