import { ensureMongoDocumentToObject } from "../utils/ensureMongoDocumentToObject.mjs";
import { GenericError } from "../utils/errors/ErrorsClass.mjs";
import { getOrganizationById } from "./organization.service.mjs";
import { ProjectModel } from "../models/project.model.mjs";
import { getUserById } from "./user.service.mjs";
import { mapUserToAPI } from "../utils/mapper/user.mjs";
import { mapOrganizationToAPI } from "../utils/mapper/organization.mjs";
import { mapProjectToAPI } from "../utils/mapper/project.mjs";
import { OAuth2AccessTokenModel } from "../models/oAuth2.model.mjs";
import { getTokenExpireAt } from "../utils/oAuth2.mjs";
import { randomBytes } from "node:crypto";

//#region src/services/oAuth2.service.ts
/**
* Function to generate client credentials
*
* @returns The client id and client secret
*/
const generateClientCredentials = () => {
	return {
		clientId: randomBytes(16).toString("hex"),
		clientSecret: randomBytes(32).toString("hex")
	};
};
/**
* Method to get the client and the project
*
* @param clientId - The client id
* @param clientSecret - The client secret
* @returns The an object containing the client, the rights and the project or false if not found
*/
const getClientAndProjectByClientId = async (clientId) => {
	const project = await ProjectModel.findOne({ "oAuth2Access.clientId": clientId });
	if (!project) return false;
	const oAuth2Access = project.oAuth2Access.find((access) => access.clientId === clientId);
	if (!oAuth2Access) return false;
	return {
		client: {
			id: oAuth2Access.clientId,
			clientId,
			clientSecret: oAuth2Access.clientSecret,
			grants: ["client_credentials"]
		},
		oAuth2Access,
		grants: oAuth2Access.grants,
		project
	};
};
/**
* Get the client and verify that the client secret is correct
*
* @param clientId - The client id
* @param clientSecret - The client secret
* @returns The client or false if not found
*/
const getClient = async (clientId, clientSecret) => {
	const result = await getClientAndProjectByClientId(clientId);
	if (!result) return false;
	const { client } = result;
	if (!client || client.clientSecret !== clientSecret) return false;
	return client;
};
/**
* Format an OAuth2Token
*
* @param token - The token to format
* @param client - The client
* @param user - The user
* @param project - The project
* @param organization - The organization
* @param grants - The grants
* @returns The formatted token
*/
const formatOAuth2Token = (token, client, user, project, organization, grants) => {
	const { clientId, userId, ...restToken } = token;
	if (String(userId) !== String(user.id)) throw new GenericError("USER_ID_MISMATCH");
	return {
		...restToken,
		client,
		user: mapUserToAPI(user),
		organization: mapOrganizationToAPI(organization),
		project: mapProjectToAPI(project),
		accessToken: token.accessToken,
		accessTokenExpiresAt: token.accessTokenExpiresAt ?? /* @__PURE__ */ new Date("999-99-99"),
		grants
	};
};
/**
* Format a auth token for the database
*
* @param token - The oAuth2 token to format
* @param clientId - The client ID
* @param userId - The user ID
* @returns
*/
const formatDBToken = (token, clientId, userId) => {
	return {
		id: token.id,
		clientId,
		userId,
		accessToken: token.accessToken,
		expiresIn: token.accessTokenExpiresAt ?? getTokenExpireAt()
	};
};
/**
* Method to save the token
*
* @param token - The token
* @param client - The client
* @param user - The user
* @returns The saved token or false if not saved
*/
const saveToken = async (token, client, user) => {
	const formattedAccessToken = formatDBToken(token, client.id, user.id);
	const result = await OAuth2AccessTokenModel.create(formattedAccessToken);
	if (!result) return false;
	const result2 = await getClientAndProjectByClientId(result.clientId);
	if (!result2) return false;
	const { project } = result2;
	const organization = await getOrganizationById(project.organizationId);
	if (!organization) return false;
	return formatOAuth2Token(formattedAccessToken, client, user, project, organization, token.rights);
};
/**
* Method to get the access token
*
* @param accessToken - The access token
* @returns The access token or false if not found
*/
const getAccessToken = async (accessToken) => {
	const token = await OAuth2AccessTokenModel.findOne({ accessToken });
	if (!token) return false;
	const { userId, clientId } = token;
	const user = await getUserById(userId);
	if (!user) return false;
	const result = await getClientAndProjectByClientId(clientId);
	if (!result) return false;
	const { client, project, grants } = result;
	const organization = await getOrganizationById(project.organizationId);
	if (!organization) return false;
	return formatOAuth2Token(token, client, user, project, organization, grants);
};
/**
* Method to get the user from the client
*
* @param client - The client
* @returns The user or false if not found
*/
const getUserFromClient = async (client) => {
	const response = await getClientAndProjectByClientId(client.id);
	if (!response) return false;
	const { userId } = response.oAuth2Access;
	if (!userId) return false;
	return await getUserById(userId) ?? false;
};
/**
* Method to verify the permissions (grants)
*
* @param token - The token
* @param scope - The scope
* @returns True if the token has the required scope, false otherwise
*/
const verifyScope = async (_token, _scope, _callback) => {
	return true;
};
/**
* Validate OAuth2 access token and return user context
*/
const validateOAuth2AccessToken = async (accessToken) => {
	try {
		const token = await OAuth2AccessTokenModel.findOne({ accessToken });
		if (!token) throw new GenericError("INVALID_ACCESS_TOKEN");
		if (/* @__PURE__ */ new Date() > new Date(token.expiresIn)) throw new GenericError("EXPIRED_ACCESS_TOKEN");
		return ensureMongoDocumentToObject(token);
	} catch (_error) {
		throw new GenericError("INVALID_ACCESS_TOKEN");
	}
};
/**
* Validate OAuth2 access token and return user context
*/
const getOAuth2AccessTokenContext = async (token) => {
	const { userId, clientId } = token;
	const user = await getUserById(String(userId));
	const result = await getClientAndProjectByClientId(clientId);
	if (!result) throw new GenericError("INVALID_ACCESS_TOKEN");
	const { project, grants } = result;
	const organization = await getOrganizationById(project.organizationId);
	return {
		accessToken: token.accessToken,
		user: user ? mapUserToAPI(user) : void 0,
		project: project ? mapProjectToAPI(project) : void 0,
		organization: organization ? mapOrganizationToAPI(organization) : void 0,
		grants
	};
};

//#endregion
export { formatDBToken, formatOAuth2Token, generateClientCredentials, getAccessToken, getClient, getClientAndProjectByClientId, getOAuth2AccessTokenContext, getUserFromClient, saveToken, validateOAuth2AccessToken, verifyScope };
//# sourceMappingURL=oAuth2.service.mjs.map