var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
import { publish, subscribe } from 'pubsub-js';
import { isExpiredJWT, createPopup, getHost } from '../utils';
import { User } from './user';
import { AuthProvider } from './provider';
export class AuthService {
    constructor(app) {
        this.SHEETBASE_USER_CHANGED = 'SHEETBASE_USER_CHANGED';
        this.SHEETBASE_USER_INFO = 'user_info';
        this.SHEETBASE_USER_CREDS = 'user_creds';
        this.currentUser = null;
        this.app = app;
        this.Api = this.app.Api
            .addBeforeHooks((data) => __awaiter(this, void 0, void 0, function* () {
            if (!!this.currentUser) {
                data.query['idToken'] = yield this.currentUser.getIdToken();
            }
            return data;
        }))
            .extend()
            .setEndpoint(this.app.options.authEndpoint || 'auth');
        // initial change state (signin locally)
        setTimeout(() => this.signInWithLocalUser(), 1000);
    }
    onAuthStateChanged(next) {
        subscribe(this.SHEETBASE_USER_CHANGED, (msg, user) => next(user));
    }
    checkActionCode(code) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Api.get('/oob', { oobCode: code });
        });
    }
    createUserWithEmailAndPassword(email, password) {
        return __awaiter(this, void 0, void 0, function* () {
            const { info, idToken, refreshToken } = yield this.Api.put('/', {}, {
                email, password, offlineAccess: true,
            });
            const user = yield this.signIn(info, idToken, refreshToken);
            return { user };
        });
    }
    signInWithEmailAndPassword(email, password) {
        return __awaiter(this, void 0, void 0, function* () {
            let user;
            if (!!this.currentUser) {
                user = this.currentUser;
            }
            else {
                const { info, idToken, refreshToken } = yield this.Api.post('/', {}, {
                    email, password, offlineAccess: true,
                });
                user = yield this.signIn(info, idToken, refreshToken);
            }
            return { user };
        });
    }
    signInWithCustomToken(token) {
        return __awaiter(this, void 0, void 0, function* () {
            let user;
            if (!!this.currentUser) {
                user = this.currentUser;
            }
            else {
                const { info, idToken, refreshToken } = yield this.Api.post('/', {}, {
                    customToken: token, offlineAccess: true,
                });
                user = yield this.signIn(info, idToken, refreshToken);
            }
            return { user };
        });
    }
    signInAnonymously() {
        return __awaiter(this, void 0, void 0, function* () {
            let user;
            if (!!this.currentUser) {
                user = this.currentUser;
            }
            else {
                const { info, idToken, refreshToken } = yield this.Api.put('/', {}, {
                    offlineAccess: true,
                });
                user = yield this.signIn(info, idToken, refreshToken);
            }
            return { user };
        });
    }
    sendPasswordResetEmail(email) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Api.put('/oob', {}, { mode: 'resetPassword', email });
        });
    }
    verifyPasswordResetCode(code) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Api.get('/oob', { oobCode: code, mode: 'resetPassword' });
        });
    }
    confirmPasswordReset(code, newPassword) {
        return __awaiter(this, void 0, void 0, function* () {
            return yield this.Api.post('/oob', {}, {
                oobCode: code,
                mode: 'resetPassword',
                newPassword,
            });
        });
    }
    signInWithPopup(provider) {
        return __awaiter(this, void 0, void 0, function* () {
            if (!this.currentUser) {
                const providerConfig = !!this.app.options.authProviders ?
                    this.app.options.authProviders[provider.providerId] : null;
                if (!!providerConfig) {
                    // remember provider
                    this.oauthProvider = provider;
                    // add handler to parent window
                    window['handleOauthResult'] = (fragment) => this.handleOauthResult(fragment);
                    // process request
                    const { clientId, redirectUri } = providerConfig;
                    return createPopup({
                        url: provider.url(clientId, redirectUri || (getHost() + '/__/auth/handler')),
                    });
                }
            }
        });
    }
    googleAuthProvider() {
        return new AuthProvider('google.com', 'https://accounts.google.com/o/oauth2/v2/auth', 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile');
    }
    facebookAuthProvider() {
        return new AuthProvider('facebook.com', 'https://www.facebook.com/v3.2/dialog/oauth', 'email');
    }
    signOut() {
        return __awaiter(this, void 0, void 0, function* () {
            this.currentUser = null;
            // notify user change
            publish(this.SHEETBASE_USER_CHANGED, null);
            // remove user info & id token & refresh token from local
            yield this.app.Localstorage.remove(this.SHEETBASE_USER_INFO);
            yield this.app.Localstorage.remove(this.SHEETBASE_USER_CREDS);
        });
    }
    signIn(info, idToken, refreshToken) {
        return __awaiter(this, void 0, void 0, function* () {
            const { uid } = info;
            this.currentUser = new User(this.Api, info, idToken, refreshToken);
            // notify user change
            publish(this.SHEETBASE_USER_CHANGED, this.currentUser);
            // save user info & id token & refresh token to local
            yield this.app.Localstorage.set(this.SHEETBASE_USER_INFO, info);
            yield this.app.Localstorage.set(this.SHEETBASE_USER_CREDS, { uid, idToken, refreshToken });
            return this.currentUser;
        });
    }
    signInWithLocalUser() {
        return __awaiter(this, void 0, void 0, function* () {
            // retrieve local creds and info
            const creds = yield this.app.Localstorage.get(this.SHEETBASE_USER_CREDS);
            let info = yield this.app.Localstorage.get(this.SHEETBASE_USER_INFO);
            // log user in
            if (!!creds && !!info && creds.uid === info.uid) {
                let idToken = creds.idToken;
                // caching check
                const beenSeconds = (new Date().getTime() - new Date(info.lastLogin).getTime()) / 1000;
                if (beenSeconds > 86400) { // info expired, over 1 day
                    // renew idToken if expired
                    if (isExpiredJWT(idToken)) {
                        const expiredUser = new User(this.Api, info, idToken, creds.refreshToken);
                        idToken = yield expiredUser.getIdToken();
                    }
                    // fetch new info
                    info = yield this.Api.get('/user', { idToken });
                }
                // sign user in
                this.signIn(info, idToken, creds.refreshToken);
            }
            else {
                // notify initial state changed
                publish(this.SHEETBASE_USER_CHANGED, null);
            }
        });
    }
    handleOauthResult(result) {
        return __awaiter(this, void 0, void 0, function* () {
            // if result available
            if (result !== '') {
                // process query
                const query = result.substr(1).split('&');
                // Parse the URI hash to fetch the access token
                const credential = {};
                for (let i = 0; i < query.length; i++) {
                    const [key, value = null] = query[i].split('=');
                    let finalValue = value;
                    if (key === 'scope') {
                        finalValue = value.split('%20');
                    }
                    else if (key === 'expires_in') {
                        finalValue = +value;
                    }
                    credential[key] = finalValue;
                }
                // get user profile
                if (!credential.access_token) {
                    throw new Error('Sheetbase oauth error!'); // no access token
                }
                else {
                    return yield this.processOauthResult(credential);
                }
            }
        });
    }
    processOauthResult(credential) {
        return __awaiter(this, void 0, void 0, function* () {
            const { access_token: accessToken } = credential;
            const { providerId } = this.oauthProvider;
            const { info, idToken, refreshToken } = yield this.Api.get('/oauth', { providerId, accessToken });
            const user = yield this.signIn(info, idToken, refreshToken);
            return { user };
        });
    }
}
