"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.IccUserXApi = void 0;
const IccUserApi_1 = require("../icc-api/api/IccUserApi");
const AuthenticationProvider_1 = require("./auth/AuthenticationProvider");
const Connection_1 = require("../icc-api/model/Connection");
const utils_1 = require("./utils");
const collection_utils_1 = require("./utils/collection-utils");
const XHR_1 = require("../icc-api/api/XHR");
var XHRError = XHR_1.XHR.XHRError;
class IccUserXApi extends IccUserApi_1.IccUserApi {
    constructor(host, headers, authenticationProvider = new AuthenticationProvider_1.NoAuthenticationProvider(), authApi, fetchImpl = typeof window !== 'undefined'
        ? window.fetch
        : typeof self !== 'undefined'
            ? self.fetch
            : fetch) {
        super(host, headers, authenticationProvider, fetchImpl);
        this.authApi = authApi;
        this.fetchImpl = fetchImpl;
    }
    /**
     * Retrieves the currently logged-in user. Uses a cached version by default to avoid redundant API calls.
     * @param bypassCache if true, forces a fresh fetch from the server and updates the cache; otherwise returns the cached user if available.
     * @return the current user.
     */
    getCurrentUser(bypassCache = false) {
        var _a;
        if (bypassCache)
            return (this.cachedCurrentUser = super.getCurrentUser());
        else
            return (_a = this.cachedCurrentUser) !== null && _a !== void 0 ? _a : (this.cachedCurrentUser = super.getCurrentUser());
    }
    /**
     * Modifies an existing user. If the user being modified is the current user, the local cache is updated accordingly.
     * Handles 409 conflict errors by re-fetching the latest revision and retrying when the only server-side changes
     * are to authenticationTokens or rev.
     * @param body the user with updated fields to persist.
     * @return the modified user as returned by the server.
     */
    modifyUser(body) {
        const _super = Object.create(null, {
            modifyUser: { get: () => super.modifyUser },
            getCurrentUser: { get: () => super.getCurrentUser }
        });
        return __awaiter(this, void 0, void 0, function* () {
            //If we do not load the current user, we cannot know if the modification is on the current user
            yield this.getCurrentUser();
            if (this.cachedCurrentUser && (yield this.cachedCurrentUser).id === (body === null || body === void 0 ? void 0 : body.id)) {
                try {
                    const modifiedUser = yield _super.modifyUser.call(this, body).catch((e) => __awaiter(this, void 0, void 0, function* () {
                        //It is
                        if (e.statusCode === 409) {
                            let userInDb = yield _super.getCurrentUser.call(this);
                            if ((0, collection_utils_1.objectEquals)((yield this.cachedCurrentUser), userInDb, ['authenticationTokens', 'rev'])) {
                                return yield _super.modifyUser.call(this, Object.assign(Object.assign({}, body), { rev: userInDb.rev, authenticationTokens: userInDb.authenticationTokens }));
                            }
                        }
                        throw e;
                    }));
                    this.cachedCurrentUser = Promise.resolve(modifiedUser);
                    return modifiedUser;
                }
                catch (e) {
                    this.cachedCurrentUser = undefined;
                    throw e;
                }
            }
            else
                return _super.modifyUser.call(this, body);
        });
    }
    /**
     * Subscribes to real-time user events using a WebSocket connection.
     * @param eventTypes the types of events to listen for (e.g. 'CREATE', 'UPDATE', 'DELETE').
     * @param filter an optional filter to restrict which user events trigger the callback.
     * @param eventFired the callback function invoked when a matching user event is received.
     * @param options optional subscription configuration such as connection parameters and retry behaviour.
     * @return a connection object that can be used to manage the WebSocket subscription lifecycle.
     */
    subscribeToUserEvents(eventTypes_1, filter_1, eventFired_1) {
        return __awaiter(this, arguments, void 0, function* (eventTypes, filter, eventFired, options = {}) {
            const rs = yield (0, utils_1.subscribeToEntityEvents)(this.host, this.authApi, 'User', eventTypes, filter, eventFired, options);
            return new Connection_1.ConnectionImpl(rs);
        });
    }
    /**
     * Checks whether the given password is valid for the currently logged-in user by attempting authentication.
     * @param password the password to verify.
     * @return true if the password is valid, false otherwise.
     */
    checkPassword(password) {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const userInfo = yield this.getGroupAndUserIdFromToken();
            if (!!userInfo) {
                console.log(this.authApi);
                const loginUserId = !!userInfo.groupId ? `${userInfo.groupId}/${userInfo.userId}` : userInfo.userId;
                try {
                    return (_a = (yield this.authApi.login({ username: loginUserId, password: password })).successful) !== null && _a !== void 0 ? _a : false;
                }
                catch (e) {
                    return e instanceof XHRError && e.statusCode == 417;
                }
            }
            throw Error('Could not get current group and user from jwt');
        });
    }
    getGroupAndUserIdFromToken() {
        return __awaiter(this, void 0, void 0, function* () {
            var _a;
            const token = (_a = (yield this.authenticationProvider.getIcureTokens())) === null || _a === void 0 ? void 0 : _a.token;
            if (!token)
                return undefined;
            const splitToken = token.split('.');
            if (splitToken.length != 3)
                return undefined;
            try {
                const tokenString = (0, utils_1.a2b)(splitToken[1]);
                const parsedClaims = JSON.parse(tokenString);
                if (!!parsedClaims['u']) {
                    return { groupId: parsedClaims['g'], userId: parsedClaims['u'] };
                }
                else
                    return undefined;
            }
            catch (_b) {
                return undefined;
            }
        });
    }
}
exports.IccUserXApi = IccUserXApi;
//# sourceMappingURL=icc-user-x-api.js.map