import { Permission } from '@api-client/core/models/store/Permission.js';
import { Service } from '../core/Service.js';
import services from './api_service.js';
var CheckState;
(function (CheckState) {
    CheckState[CheckState["NotChecked"] = 0] = "NotChecked";
    CheckState[CheckState["Checking"] = 1] = "Checking";
    CheckState[CheckState["Checked"] = 2] = "Checked";
})(CheckState || (CheckState = {}));
const UserCacheKey = 'current_user';
export default class UserService extends Service {
    static key = 'api-service/user';
    /**
     * Tells yhe current state of checking for user session.
     */
    status = CheckState.NotChecked;
    /**
     * The currently logged in user.
     */
    #user = null;
    /**
     * The list of groups the user belongs to.
     */
    #groups = [];
    /**
     * A hidden value for the `updateComplete` getter.
     */
    #updateComplete;
    /**
     * The resolver to call when the update completes.
     */
    #updateResolver;
    /**
     * Checks whether user is logged in and exists.
     * @returns `true`
     */
    get isLoggedIn() {
        return !!this.#user;
    }
    async onStart() {
        // do this asynchronously to avoid blocking the app startup
        const logger = await services.get('logger');
        logger.debug('[UserService]: Starting service');
        this.load();
    }
    async onDestroy() {
        this.#groups = [];
        this.#user = null;
        const logger = await services.get('logger');
        logger.debug('[UserService]: Destroying service');
    }
    /**
     * Ensures that the user data is loaded.
     * @returns A promise that resolves when the user data is loaded.
     */
    async ready() {
        if (this.status === CheckState.Checking) {
            await this.#updateComplete;
            return;
        }
        if (this.status === CheckState.NotChecked) {
            await this.load();
        }
    }
    /**
     * Loads the user data from the API.
     * This gets the user data from the API and the list of groups the user belongs to.
     *
     * Note that calling this method multiple times will refresh the user data
     * and the list of groups, so it is recommended to call this method only once
     * and then use the `getUser()` method to get the user data.
     * @returns A promise that resolves when the user data is loaded.
     */
    async load() {
        const logger = await services.get('logger');
        logger.debug('[UserService]: Loading user data');
        if (this.status === CheckState.Checking) {
            logger.debug('[UserService]: Already checking user data');
            return this.#updateComplete;
        }
        logger.debug('[UserService]: Starting to check user data');
        this.status = CheckState.Checking;
        this.#updateComplete = new Promise((resolver) => {
            this.#updateResolver = resolver;
        });
        try {
            logger.debug('[UserService]: Loading user and groups');
            await this.#loadUser();
            await this.#loadGroups();
            this.status = CheckState.Checked;
        }
        catch (error) {
            // ...ignore errors here, they are handled in the loading methods
            this.status = CheckState.NotChecked;
            logger.error('[UserService]: Error loading user data', error);
        }
        logger.debug('[UserService]: Finished loading user data');
        this.#updateResolver?.();
        this.#updateResolver = undefined;
        this.#updateComplete = undefined;
        this.dispatchEvent(new Event('userloaded'));
    }
    async getUserOrThrow() {
        const user = await this.getUser();
        if (!user) {
            throw new Error('The user session is not available. Please log in again.');
        }
        return user;
    }
    async getUser() {
        if (this.status === CheckState.Checking) {
            await this.#updateComplete;
        }
        return this.#user;
    }
    async getGroups() {
        if (this.status === CheckState.Checking) {
            await this.#updateComplete;
            return this.#groups;
        }
        return this.#groups;
    }
    async #loadUser() {
        const logger = await services.get('logger');
        const sdk = await services.get('sdk');
        const cache = await services.get('cache');
        const cachedUser = await cache.get(UserCacheKey);
        if (cachedUser) {
            this.#user = cachedUser;
            return;
        }
        try {
            this.#user = await sdk.user.me();
            logger.debug('[UserService]: Has user data');
            await cache.set(UserCacheKey, this.#user, cache.TTL.OneHour);
        }
        catch (error) {
            logger.error('[UserService]: Failed to load user data', error);
            this.#user = null;
        }
    }
    async #loadGroups() {
        const cache = await services.get('cache');
        const logger = await services.get('logger');
        const orgService = await services.get('org');
        const org = await orgService.currentOrganization();
        const uid = this.#user?.key;
        if (!org || !uid) {
            logger.info('UserService: No organization or user ID found, cannot load groups.');
            this.#groups = [];
            return;
        }
        const cachedGroups = await cache.get(`user_groups_${org.key}`);
        if (cachedGroups) {
            this.#groups = cachedGroups;
            return;
        }
        const sdk = await services.get('sdk');
        try {
            const result = await sdk.groups.list(org.key, { uid });
            this.#groups = result.items;
            logger.debug(`[UserService]: Loaded ${this.#groups.length} groups`);
            await cache.set(`user_groups_${org.key}`, this.#groups, cache.TTL.OneHour);
        }
        catch (error) {
            logger.error('UserService: Failed to load groups', error);
            this.#groups = [];
        }
    }
    /**
     * Finds the user's highest permission in the given list of permissions.
     * @param permissions The list of permissions to search in.
     * @return The user's permission if found, otherwise `undefined`.
     */
    findUserPermission(permissions) {
        const uid = this.#user?.key;
        const relevantPermissions = [];
        for (const p of permissions) {
            if (p.granteeId === uid) {
                relevantPermissions.push(new Permission(p));
            }
            else if (p.type === 'group') {
                const group = this.#groups.find((g) => g.key === p.granteeId);
                if (group) {
                    relevantPermissions.push(new Permission(p));
                }
            }
            else if (p.type === 'organization') {
                relevantPermissions.push(new Permission(p));
            }
        }
        if (relevantPermissions.length === 0) {
            return undefined;
        }
        relevantPermissions.sort((a, b) => (Permission.isHigherRole(a.role, b.role) ? -1 : 1));
        return relevantPermissions[0].toJSON();
    }
}
//# sourceMappingURL=UserService.js.map