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());
    });
};
import '@iotize/tap/service/impl/group';
import { isCodeError } from '@iotize/common/error';
import { TapError } from '@iotize/tap';
import { ResultCode } from '@iotize/tap/client/api';
import { BehaviorSubject } from 'rxjs';
import { BasicAuth } from './basic-auth';
import { INITIAL_SESSION_STATE } from './config';
import { debug } from './debug';
import { ScramAuth } from './scram-auth';
import { TapAuthError } from './tap-auth-error';
const TAG = 'TapAuth';
export class TapAuth {
    constructor(tap) {
        this.tap = tap;
        this._sessionState = new BehaviorSubject(INITIAL_SESSION_STATE);
        this._sessionData = new BehaviorSubject({});
    }
    /**
     * Get current session state snapshot
     */
    get sessionStateSnapshot() {
        return this._sessionState.value;
    }
    /**
     * Listen to session state changed
     */
    get sessionState() {
        return this._sessionState.asObservable();
    }
    /**
     * Get current session data snapshot
     */
    get sessionDataSnapshot() {
        return this._sessionData.value;
    }
    /**
     * Listen to session data changed
     */
    get sessionData() {
        return this._sessionData.asObservable();
    }
    get service() {
        return this.tap.service;
    }
    setAuthMethod(method) {
        this._auth = method;
    }
    /**
     * Clear auth cache.
     * Usefull for example if Tap configuration has changed
     */
    clearCache() {
        this._auth = undefined;
    }
    // async test() {
    //     const lockInfo = await this.getLockInfo();
    //     if (userPassword) {
    //         if (currentUserId === undefined) {
    //             currentUserId = (await this.service.interface.getCurrentGroupId()).body();
    //         }
    //         const currentUsername = (await this.tap.service.group.getName(
    //             currentUserId!
    //         )).body()!;
    //         await this.login(currentUsername, userPassword, false);
    //     }
    // }
    login(params, options) {
        return __awaiter(this, void 0, void 0, function* () {
            debug(TAG, `Tap login as ${params.username}...`);
            const auth = yield this.setupTapAuthEngineIfRequired();
            try {
                const sessionData = yield auth.login(params);
                if (sessionData && sessionData.key) {
                    this.tap.encryption.sessionKey = sessionData.key;
                    this.tap.encryption.resume();
                }
                this._sessionData.next(sessionData);
                if (!(options === null || options === void 0 ? void 0 : options.noRefreshSessionState)) {
                    yield this.refreshSessionState({
                        username: params.username
                    });
                }
                const event = {
                    type: 'tap-login'
                };
                this.tap.notifyEvent(event);
                return sessionData;
            }
            catch (err) {
                if (isCodeError(TapError.Code.ResponseStatusError, err)) {
                    if (err.response.status === ResultCode.NOT_ACCEPTABLE ||
                        err.response.status === ResultCode.UNAUTHORIZED) {
                        throw TapAuthError.invalidCredentialsError(params, err);
                    }
                    else if (err.response.status === ResultCode.SERVICE_UNAVAILABLE) {
                        throw TapAuthError.tooManyLoginFailedError(params, err);
                    }
                }
                throw TapAuthError.tapRequestError(params, err);
            }
        });
    }
    /**
     * Tap logout
     * Reject if logout failed
     */
    logout() {
        return __awaiter(this, void 0, void 0, function* () {
            debug(TAG, `Tap logout...`);
            const auth = yield this.setupTapAuthEngineIfRequired();
            yield auth.logout();
            // we keep session keys even after logout
            this._sessionState.next(INITIAL_SESSION_STATE);
            this._sessionData.next({});
            const event = {
                type: 'tap-logout'
            };
            this.tap.notifyEvent(event);
        });
    }
    /**
     * Read session state from the Tap
     * This will perform a few tap requests
     *
     * TODO we should invalidate encryption key if session state changed
     * TODO we should use multi commands to improve performance
     */
    refreshSessionState(defaults) {
        return __awaiter(this, void 0, void 0, function* () {
            debug(TAG, '[TAP]', `Refreshing session state (current=${this.sessionStateSnapshot.name})...`);
            const groupId = (yield this.service.interface.getCurrentGroupId()).body();
            if (groupId !== this.sessionStateSnapshot.groupId) {
                const currentSessionState = Object.assign({}, this.sessionStateSnapshot);
                currentSessionState.groupId = groupId;
                currentSessionState.name = (defaults === null || defaults === void 0 ? void 0 : defaults.username) || (yield this.service.group.getName(groupId)).body();
                currentSessionState.lifeTime = (yield this.service.group.getSessionLifetime(groupId)).body();
                debug(TAG, '[TAP]', `Notifying new session state: ${currentSessionState.name}`);
                this._sessionState.next(currentSessionState);
            }
            else {
                debug(TAG, '[TAP]', `Session state did not changed: ${this.sessionStateSnapshot.name} (id=${groupId})`);
            }
            return this.sessionStateSnapshot;
        });
    }
    // private _isLoggedIn(): boolean {
    //     return (
    //         this.sessionStateSnapshot != undefined &&
    //         this.sessionStateSnapshot.name !==
    //         INITIAL_SESSION_STATE.name
    //     );
    // }
    /**
     * Change password for given user. If userIdOrName is not set, it will change password for the currenty
     * connected user.
     * @param newPassword
     * @param userIdOrUndefined user identifier for which password will be change
     */
    changePassword(newPassword, userIdOrUndefined) {
        return __awaiter(this, void 0, void 0, function* () {
            // When tap will be able to change password by username, we will be able to pass directly the username instead of the userId
            let userId;
            let username;
            if (userIdOrUndefined === undefined) {
                userId = (yield this.service.interface.getCurrentGroupId()).body();
                username = (yield this.service.group.getName(userId)).body();
            }
            else {
                // TODO for now we cannot get the user id from the username 
                // if (typeof userIdOrName === "string") {
                //     username = userIdOrName;
                //     userId = (await this.service.group.getUserIdFromName(username)).body();
                // }
                // else {
                userId = userIdOrUndefined;
                username = (yield this.service.group.getName(userId)).body();
                // }
            }
            const auth = yield this.setupTapAuthEngineIfRequired();
            yield auth.changePassword(newPassword, userId);
            const event = {
                type: 'tap-user-password-change',
                user: {
                    id: userId,
                    username
                }
            };
            this.tap.notifyEvent(event);
        });
    }
    setupTapAuthEngineIfRequired() {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this._auth) {
                this._auth = yield this.setupTapAuthEngine();
            }
            return this._auth;
        });
    }
    setupTapAuthEngine() {
        return __awaiter(this, void 0, void 0, function* () {
            const securityOptions = (yield this.service.interface.getSecurityOptions()).body();
            debug(TAG, `securityOptions ${JSON.stringify(securityOptions)}`);
            if (securityOptions.scramActivated) {
                debug(TAG, 'Setup with ScramAuth');
                return new ScramAuth(this.tap);
            }
            else {
                debug(TAG, 'Setup with BasicAuth');
                return new BasicAuth(this.tap, {
                    hashPassword: securityOptions.hashPassword
                });
            }
        });
    }
}
//# sourceMappingURL=data:application/json;base64, 