"use strict";
var __extends = (this && this.__extends) || (function () {
    var extendStatics = function (d, b) {
        extendStatics = Object.setPrototypeOf ||
            ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
            function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
        return extendStatics(d, b);
    };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
var Emitter_1 = __importDefault(require("./Emitter"));
var underscore_1 = __importDefault(require("underscore"));
var bluebird_1 = __importDefault(require("bluebird"));
var UserData_1 = __importDefault(require("./UserData"));
var Auth = /** @class */ (function (_super) {
    __extends(Auth, _super);
    function Auth(config, ippApi, storage) {
        var _this = _super.call(this) || this;
        _this.config = config;
        _this.ippApi = ippApi;
        _this.storage = storage;
        _this.polling = false;
        _this._user = {};
        _this._authenticated = false;
        _this._authInProgress = false;
        _this._selfTimeout = 15 * 1000; // @todo Should not be hardcoded
        _this.on401 = function () {
            if (_this.polling) {
                return;
            }
            var renew = _this.storage.persistent.get('renew');
            if (renew) {
                return;
            }
            // Block rest api
            _this.ippApi.block();
            // Remove access token
            _this.ippApi.tokens.access_token = '';
            _this.storage.persistent.remove('access_token');
            _this.storage.persistent.create('renew', '1');
            _this.emit(_this.EVENT_RE_LOGGING); // @todo do we need this?
            // Try to authenticate
            _this.authenticate(true)
                .then(function () {
                // @todo oh no....
                _this.storage.user.suffix = _this._user.id;
                _this.emit(_this.EVENT_LOGIN_REFRESHED);
            })
                .catch(function () {
                _this.emit(_this.EVENT_ERROR);
            })
                .finally(function () {
                _this.storage.persistent.remove('renew');
                _this.ippApi.unblock();
            });
        };
        _this._selfTimeout = underscore_1.default.random(15, 45) * 1000;
        ippApi.on(ippApi.EVENT_401, _this.on401);
        _this.UserData = new UserData_1.default(ippApi, storage, 0);
        return _this;
    }
    Object.defineProperty(Auth.prototype, "EVENT_LOGGED_IN", {
        get: function () {
            return 'logged_in';
        } // emitted when logged in (not on login refresh)
        ,
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Auth.prototype, "EVENT_RE_LOGGING", {
        get: function () {
            return 're_logging';
        } // emitted when re-logging started
        ,
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Auth.prototype, "EVENT_LOGIN_REFRESHED", {
        get: function () {
            return 'login_refreshed';
        } // emitted only after 401 and re-login
        ,
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Auth.prototype, "EVENT_LOGGED_OUT", {
        get: function () {
            return 'logged_out';
        } // emitted when logged out
        ,
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Auth.prototype, "EVENT_ERROR", {
        get: function () {
            return 'error';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Auth.prototype, "EVENT_401", {
        get: function () {
            return '401';
        } // listener
        ,
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Auth.prototype, "EVENT_USER_UPDATED", {
        get: function () {
            return 'user_updated';
        },
        enumerable: true,
        configurable: true
    });
    Object.defineProperty(Auth.prototype, "user", {
        get: function () {
            return this._user;
        },
        enumerable: true,
        configurable: true
    });
    Auth.prototype.authenticate = function (force, tokens) {
        var _this = this;
        if (force === void 0) { force = false; }
        if (tokens === void 0) { tokens = {}; }
        if (tokens && tokens.access_token) {
            this.ippApi.tokens = tokens;
        }
        var p = new bluebird_1.default(function (resolve, reject) {
            if (_this._authInProgress) {
                reject(new Error('Auth already in progress')); // @todo or resolve?
                return;
            }
            _this._authInProgress = true;
            if (_this._authenticated && !force) {
                _this._authInProgress = false;
                resolve(); // Actually at this moment we have no idea if access token is valid but we dont have to worry about that because of the 401 process from API
                return;
            }
            _this.processAuth(force)
                .then(function () {
                if (!_this._authenticated) {
                    _this._authenticated = true;
                    _this.UserData = new UserData_1.default(_this.ippApi, _this.storage, _this.user.id);
                    _this.UserData.parse(_this.user.meta_data);
                    // @todo oh no....
                    _this.storage.user.suffix = _this._user.id;
                    _this.emit(_this.EVENT_LOGGED_IN);
                    _this.startPollingSelf();
                }
                resolve();
                return true;
            })
                .catch(function (err) {
                _this.emit(_this.EVENT_ERROR, err);
                if (_this._authenticated) {
                    _this.logout(false, true);
                }
                reject(new Error(err));
            })
                .finally(function () {
                _this._authInProgress = false;
            });
        });
        return p;
    };
    Auth.prototype.login = function (username, password) {
        var _this = this;
        var p = new bluebird_1.default(function (resolve, reject) {
            _this.ippApi
                .userLogin({
                grant_type: 'password',
                client_id: _this.config.api_key,
                client_secret: _this.config.api_secret,
                email: username,
                password: password
            })
                .then(function (res) {
                _this.saveTokens(res.data);
                return _this.authenticate();
            })
                .then(function () {
                resolve();
            })
                .catch(function (err) {
                if (err.httpCode === 400 || err.httpCode === 401) {
                    switch (err.data) {
                        case 'invalid_grant':
                            err.message =
                                'The username and password you entered did not match our records. Please double-check and try again.';
                            break;
                        case 'invalid_client':
                            err.message = "Your client doesn't have access to iPushPull system.";
                            break;
                        case 'invalid_request':
                            // err.message = err.message;
                            break;
                        default:
                            // err.message = this.utils.parseApiError(err.data, "Unknown error");
                            break;
                    }
                }
                _this.emit(_this.EVENT_ERROR, err);
                reject(err);
            });
        });
        return p;
    };
    Auth.prototype.logout = function (all, ignore) {
        if (all === void 0) { all = false; }
        if (ignore === void 0) { ignore = false; }
        // Call rest endpoint to destroy tokens - don't need to wait for result
        if (!ignore) {
            this.ippApi.userLogout({ all: all });
        }
        this.ippApi.tokens = {
            access_token: '',
            refresh_token: ''
        };
        this.storage.persistent.remove('access_token');
        this.storage.persistent.remove('refresh_token');
        this.storage.persistent.remove('renew');
        this._authenticated = false;
        this._user = {};
        this.UserData.destroy();
        clearTimeout(this._selfTimer);
        // @todo oh no....
        this.storage.user.suffix = 'GUEST';
        this.emit(this.EVENT_LOGGED_OUT);
    };
    Auth.prototype.isEnterprise = function () {
        return this._authenticated ? this.user.organization.is_enterprise : false;
    };
    Auth.prototype.processAuth = function (force) {
        var _this = this;
        if (force === void 0) { force = false; }
        var p = new bluebird_1.default(function (resolve, reject) {
            var accessToken = _this.storage.persistent.get('access_token') || _this.ippApi.tokens.access_token;
            var refreshToken = _this.storage.persistent.get('refresh_token') || _this.ippApi.tokens.refresh_token;
            if (accessToken && !force) {
                _this.getUserInfo().then(resolve, reject);
            }
            else {
                if (refreshToken) {
                    _this.refreshTokens()
                        .then(function (data) {
                        _this.saveTokens(data.data);
                        _this.getUserInfo().then(resolve, reject);
                    })
                        .catch(function (err) {
                        _this.storage.persistent.remove('refresh_token');
                        reject(new Error(err));
                    });
                }
                else {
                    reject(new Error('No tokens available'));
                }
            }
        });
        return p;
    };
    Auth.prototype.refreshTokens = function () {
        var refreshToken = this.storage.persistent.get('refresh_token') || this.ippApi.tokens.refresh_token;
        return this.ippApi.refreshAccessTokens(refreshToken);
    };
    Auth.prototype.saveTokens = function (tokens) {
        this.storage.persistent.create('access_token', tokens.access_token, tokens.expires_in / 86400);
        this.storage.persistent.create('refresh_token', tokens.refresh_token, 365);
        this.ippApi.tokens = tokens;
        this.storage.persistent.remove('renew');
    };
    Auth.prototype.getUserInfo = function () {
        var _this = this;
        var p = new bluebird_1.default(function (resolve, reject) {
            _this.ippApi.getSelfInfo().then(function (res) {
                if (!underscore_1.default.isEqual(_this._user, res.data)) {
                    var updated = _this._user.id;
                    _this._user = res.data; // going outside function scope..
                    _this.storage.user.suffix = _this._user.id;
                    _this.UserData.parse(_this.user.meta_data);
                    if (updated)
                        _this.emit(_this.EVENT_USER_UPDATED);
                }
                resolve();
            }, reject);
        });
        return p;
    };
    Auth.prototype.startPollingSelf = function () {
        var _this = this;
        clearTimeout(this._selfTimer);
        this._selfTimer = setTimeout(function () {
            _this.getUserInfo()
                .then()
                .finally(function () {
                _this.startPollingSelf();
            });
        }, this._selfTimeout);
    };
    Auth.$inject = [
        '$q',
        '$timeout',
        'ippApiService',
        'ippStorageService',
        'ippConfig',
        'ippUtilsService'
    ];
    return Auth;
}(Emitter_1.default));
exports.Auth = Auth;
//# sourceMappingURL=Auth.js.map