var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { BaseEngine, RegisterForStartup } from "@memberjunction/core";
import { UUIDsEqual } from "@memberjunction/global";
/**
 * Resource Permission Engine is used for accessing metadata about permissions for resources and also determining if a user has access to a resource and at what level.
 */
let ResourcePermissionEngine = class ResourcePermissionEngine extends BaseEngine {
    /**
     * Returns the global instance of the class. This is a singleton class, so there is only one instance of it in the application. Do not directly create new instances of it, always use this method to get the instance.
     */
    static get Instance() {
        return super.getInstance();
    }
    async Config(forceRefresh, contextUser, provider) {
        const c = [
            {
                Type: 'entity',
                EntityName: 'MJ: Resource Permissions',
                PropertyName: "_Permissions",
                CacheLocal: true
            },
            {
                Type: 'dataset',
                DatasetName: 'ResourceTypes',
                PropertyName: "_ResourceTypes",
                DatasetResultHandling: "single_property"
            }
        ];
        await super.Load(c, provider, forceRefresh, contextUser);
    }
    get ResourceTypes() {
        return this._ResourceTypes?.ResourceTypes ?? [];
    }
    get Permissions() {
        return this._Permissions ?? [];
    }
    /**
     * Case-insensitive name → ID lookup against the cached `MJ: Resource Types`
     * catalog. Returns null when the catalog isn't loaded yet or no matching
     * type exists. Used by `shareNotification.ts` and `shareNotificationHandler.ts`
     * to resolve the `ResourceTypeID` for notification deep-links.
     */
    ResourceTypeIdByName(name) {
        if (!name)
            return null;
        const lower = name.trim().toLowerCase();
        const match = this._ResourceTypes?.ResourceTypes?.find((rt) => rt.Name?.trim().toLowerCase() === lower);
        return match?.ID ?? null;
    }
    /**
     * Convenience method to find all of the permissions for a given Resource Type and Resource Record ID, no additional filtering takes place in this method regarding
     * the status or level of the permission.
     * @param ResourceTypeID
     * @param ResourceRecordID
     * @returns
     */
    GetResourcePermissions(ResourceTypeID, ResourceRecordID) {
        return this.Permissions.filter((r) => UUIDsEqual(r.ResourceTypeID, ResourceTypeID) && UUIDsEqual(r.ResourceRecordID, ResourceRecordID));
    }
    /**
     * Determines the highest level of resource permission a user has for a specific resource.
     * This function checks both user-specific permissions and permissions assigned through roles
     * and returns the highest level of permission ('View', 'Edit', 'Owner', or `null` if no permission is found).
     * Note: This only returns MJResourcePermissionEntity objects that have Status === Approved.
     *
     * @param {string} ResourceTypeID - The ID of the resource type (e.g., document, project).
     * @param {string} ResourceRecordID - The ID of the specific resource record.
     * @param {UserInfo} user - The user object containing user details, including roles and ID.
     *
     * @returns {'View' | 'Edit' | 'Owner' | null} - The highest permission level the user has for the resource,
     * or `null` if no permission exists.
     *
     * Permission Levels:
     * - 'Owner': Full control over the resource.
     * - 'Edit': Can modify the resource.
     * - 'View': Can only view the resource.
     * - `null`: No permissions found for the user on this resource.
     */
    GetUserResourcePermissionLevel(ResourceTypeID, ResourceRecordID, user) {
        // Get all permissions for the specified resource
        const allPermissions = this.GetResourcePermissions(ResourceTypeID, ResourceRecordID);
        // Filter permissions specifically granted to the user
        const userPermissions = allPermissions.filter((p) => p.Type === 'User' && UUIDsEqual(p.UserID, user.ID));
        // Filter permissions granted through roles the user belongs to
        const rolePermissions = allPermissions.filter((p) => p.Type === 'Role' &&
            user.UserRoles.find(ur => UUIDsEqual(ur.RoleID, p.RoleID)) !== undefined);
        // Combine user-specific permissions and role-based permissions
        const allPermissionsForUser = userPermissions.concat(rolePermissions);
        const approvedPermissionsForUser = allPermissionsForUser.filter((p) => p.Status === 'Approved');
        // If no permissions are found, return null
        if (approvedPermissionsForUser.length === 0) {
            return null;
        }
        else {
            // Reduce permissions to find the highest permission level ('Owner' > 'Edit' > 'View')
            return approvedPermissionsForUser.reduce((prev, current) => {
                if (current.PermissionLevel === 'Owner') {
                    return 'Owner'; // 'Owner' has the highest priority
                }
                else if (current.PermissionLevel === 'Edit' && (prev === 'View' || prev === null)) {
                    return 'Edit'; // 'Edit' has a higher priority than 'View'
                }
                else {
                    return prev; // Keep the previous lower priority permission ('View' or null)
                }
            }, 'View');
        }
    }
    /**
     * Returns all the permissions a user has for a specific resource type based on both their user-specific permissions and permissions assigned through roles.
     * This only returns MJResourcePermissionEntity objects that have Status === Approved.
     * @param user
     * @param ResourceTypeID
     * @returns
     */
    GetUserAvailableResources(user, ResourceTypeID) {
        let rolePermissions = this.Permissions.filter((r) => r.Type === 'Role' && user.UserRoles.find(ur => UUIDsEqual(ur.RoleID, r.RoleID)) !== undefined);
        let permissions = this.Permissions.filter((r) => r.Type === 'User' && UUIDsEqual(r.UserID, user.ID));
        if (ResourceTypeID) {
            permissions = permissions.filter((r) => UUIDsEqual(r.ResourceTypeID, ResourceTypeID));
            rolePermissions = rolePermissions.filter((r) => UUIDsEqual(r.ResourceTypeID, ResourceTypeID));
        }
        permissions = permissions.concat(rolePermissions);
        // now filter to ONLY approved permissions
        permissions = permissions.filter((r) => r.Status === 'Approved');
        // now, we reduce the array so that we only have the highest permission level for each resourcetypeid/resourcerecordid combination
        let reducedPermissions = [];
        permissions.forEach((p) => {
            let existing = reducedPermissions.find((r) => UUIDsEqual(r.ResourceTypeID, p.ResourceTypeID) && UUIDsEqual(r.ResourceRecordID, p.ResourceRecordID));
            if (!existing) {
                reducedPermissions.push(p);
            }
            else {
                // existing permission and see if this one is higher, if so, replace it in the array with the new one
                let bSwap = false;
                if (p.PermissionLevel === 'Owner' && existing.PermissionLevel !== 'Owner') {
                    bSwap = true;
                }
                else if (p.PermissionLevel === 'Edit' && existing.PermissionLevel === 'View') {
                    bSwap = true;
                }
                if (bSwap) {
                    // get rid of the existing permission
                    reducedPermissions = reducedPermissions.filter((r) => r !== existing);
                    // add the new permission
                    reducedPermissions.push(p);
                }
            }
        });
        return reducedPermissions;
    }
    /**
     * This method will use the MJ metadata to find the fields names for the fields that represent the OwnerID and the Name of the resource in the given resource type.
     * Often, these fields are simply named OwnerID and Name in the underlying entity that represents the resource, but this method uses metadata lookups to find the first
     * foreign key to the Users entity from the resource type's entity and looks for the field that is consider the "Name Field" for the entity and returns those values.
     */
    GetResourceTypeInfoFields(ResourceTypeID) {
        const md = this.ProviderToUse;
        const rt = this.ResourceTypes.find((rt) => UUIDsEqual(rt.ID, ResourceTypeID));
        if (!rt)
            throw new Error(`Resource Type ${ResourceTypeID} not found`);
        const entity = md.EntityByID(rt.EntityID);
        if (!entity)
            throw new Error(`Entity ${rt.EntityID} not found`);
        const usersEntity = md.EntityByName('MJ: Users');
        if (!usersEntity)
            throw new Error(`Entity MJ: Users not found`);
        const ownerIDField = entity.Fields.find((f) => UUIDsEqual(f.RelatedEntityID, usersEntity.ID));
        const nameField = entity.NameField;
        return {
            OwnerIDFieldName: ownerIDField?.Name,
            NameFieldName: nameField?.Name,
            PrimaryKeyFieldName: entity.FirstPrimaryKey.Name
        };
    }
};
ResourcePermissionEngine = __decorate([
    RegisterForStartup()
], ResourcePermissionEngine);
export { ResourcePermissionEngine };
//# sourceMappingURL=ResourcePermissionEngine.js.map