import Swagger from 'swagger-client';
import config from './wt-config';
import emitter from './wt-emitter';

class Auth {
	
	constructor(enableDebugFlag, language = 'en-GB', storagePrefix = "", apiUrl = null) {
		
		this.ID_TOKEN = `${storagePrefix !== "" ? storagePrefix+'_':''}rwt_idToken`;
		this.ACCESS_TOKEN = `${storagePrefix !== "" ? storagePrefix+'_':''}rwt_accessToken`;
		this.REFRESH_TOKEN = `${storagePrefix !== "" ? storagePrefix+'_':''}rwt_refreshToken`;
		
		Object.assign(this, emitter());
		
		this.__log = Auth.noop;
		if (enableDebugFlag) {
			this._enableDebug();
		}
		
		this.__language = language;
		this.__refreshTokenPromise = null;
		this.__isRefreshing = false;
		this.__isLogged = null;
		this.__parsedJwt = null;
		this.__specUrl = apiUrl ? apiUrl : config.apiUrl;
		this.__client = this.initialize(true);
	}
	
	static noop() {
	
	}
	
	_enableDebug() {
		this.__log = console.log.bind(console);
	}
	
	_requestInterceptor(req) {
		if (!req.headers) {
			req.headers = {};
		}
		req.headers['Accept-Language'] = this.__language;
		return req;
	}
	
	_responseInterceptor(res, req) {
		if (res.status === 401) {
			
			if (this.__isLogged) {
				
				let rt = localStorage.getItem(this.REFRESH_TOKEN);
				this.__refreshTokenPromise = this.__isRefreshing ? this.__refreshTokenPromise : this.__client
					.then(client => {
						this.__isRefreshing = true;
						return client.apis.auth.refreshToken({}, {
							requestBody: {
								refreshToken: localStorage.getItem(this.REFRESH_TOKEN)
							}
						});
					})
					.then(r => {
						this.__isRefreshing = false;
						return this.login(r.data.idToken, r.data.accessToken, rt, true);
					})
					.catch((e) => {
						this.__isRefreshing = false;
						this.logout();
						return Promise.reject(e);
					})
				
				return this.__refreshTokenPromise
					.then(() => Swagger.http({
						...req,
						headers: {...req.headers, Authorization: 'Bearer ' + localStorage.getItem(this.ID_TOKEN)}
					}))
					.then(res => {
						if (typeof res.obj === 'object' && res.obj !== null) {
							res.data = res.obj
						}
						return res;
					})
			}
		}
		
		if (typeof res.obj === 'object' && res.obj !== null) {
			res.data = res.obj
		}
		return res;
	}
	
	initialize(isInitialEvent = false, isReauth = false) {
		
		let idToken = localStorage.getItem(this.ID_TOKEN);
		let authorizations = {};
		if (idToken) {
			authorizations['bearer'] = '' + idToken;
		}
		this.__isLogged = !!idToken;
		let _that = this;
		return new Swagger(this.__specUrl, {
			authorizations,
			requestInterceptor: this._requestInterceptor.bind(this),
			responseInterceptor: function (res) {
				return _that._responseInterceptor(res, this);
			}
		})
			.then(client => {
				this.__isLogged = !!idToken;
				this.__parsedJwt = this.parseJwt(idToken);
				this.emit(this.__isLogged ? 'login' : 'logout', {
					client,
					isInitialEvent,
					isReauth,
					isLoggedInAsDevice: this.isLoggedInAs(this.__parsedJwt, 'device'),
					isLoggedInAsObserver: this.isLoggedInAs(this.__parsedJwt, 'observer'),
					isLoggedInAsInstanceAdmin: this.isLoggedInAs(this.__parsedJwt, 'instanceAdmin'),
					isDemoAccount: this.isLoggedInAs(this.__parsedJwt, 'demo'),
					isLoggedInAsAdmin: this.isLoggedInAs(this.__parsedJwt, 'admin'),
					domain: this.getDomain(this.__parsedJwt)
				});
				return client;
			})
			.catch(e => {
				this.__isLogged = false;
				this.emit('logout', {
					client: null,
					isInitialEvent,
					isLoggedInAsDevice: false,
					isLoggedInAsObserver: false,
					isLoggedInAsInstanceAdmin: false,
					isDemoAccount: false,
					isLoggedInAsAdmin: false,
					isReauth
				});
				return Promise.reject(e);
			})
	}
	
	login(idToken, accessToken, refreshToken, isReauth = false) {
		localStorage.setItem(this.ID_TOKEN, idToken);
		localStorage.setItem(this.ACCESS_TOKEN, accessToken);
		localStorage.setItem(this.REFRESH_TOKEN, refreshToken);
		return this.__client = this.initialize(false, isReauth);
	}
	
	logout() {
		localStorage.removeItem(this.ID_TOKEN);
		localStorage.removeItem(this.ACCESS_TOKEN);
		localStorage.removeItem(this.REFRESH_TOKEN);
		return this.__client = this.initialize();
	}
	
	isLoggedIn() {
		return this.__isLogged;
	}
	
	isLoggedInAs(parsedJwt, as = 'device') {
		parsedJwt = parsedJwt || this.__parsedJwt;
		return !!(parsedJwt && parsedJwt['cognito:groups'] && parsedJwt['cognito:groups'].includes(as));
	}
	
	setLanguage(language) {
		this.__language = language;
		return this.__language;
	}
	
	getDomain(parsedJwt) {
		parsedJwt = parsedJwt || this.__parsedJwt;
		return parsedJwt['custom:domain'];
	}
	
	parseJwt(jwt) {
		if (!jwt || typeof jwt !== "string") return false;
		let match = jwt.match(/^(([0-9a-zA-Z]*)\.){2}[\w-]*$/);
		if (!match || !match[2]) return false;
		try {
			match = new Buffer(match[2], 'base64').toString('binary');
			return JSON.parse(match);
		} catch (e) {
			return false;
		}
	}
}


export default Auth;
