import { Observable, Subject } from 'rxjs';
import { RouteLocationNormalizedLoaded } from 'vue-router';
import apiService, { ApiService } from './api.service';
import { AppConfVo, AppMenuVo, ConsoleLogType, ICommonService, ILogerService, UserDetailsVo } from './common.interface';
import Icon from './icon';

import { FirebaseApp, initializeApp } from 'firebase/app';
import { getMessaging, getToken, onMessage, Messaging, deleteToken } from 'firebase/messaging';

/**
 * Shared service
 */
export class SharedService implements ILogerService {
	/**
	 * Instance  of shared service
	 */
	private static _instance: SharedService | null = null;

	/**
	 * Common service of shared service
	 */
	private _commonService: ICommonService = null;

	/**
	 * Api  of shared service
	 */
	private _api: ApiService = null;

	/**
	 * App conf of shared service
	 */
	private _appConf: AppConfVo = {
		appType: 'portal',
		appName: 'Portal',
		loginInfo: { userId: null, userNm: null, covendId: null },
		processEnv: null,
		authType: 'X',
		installType: '',
		authMenu: [],
		showLogger: false,
		deferredPrompt: null,
		addHomeScreen: null,
	};

	/**
	 * Creates an instance of shared service.
	 */
	constructor() {
		this._api = apiService;
		this._appConf.processEnv = process.env as any;
		switch (this._appConf.processEnv.VUE_APP_TYPE) {
			case 'as':
			case 'portal':
				this._appConf.appType = this._appConf.processEnv.VUE_APP_TYPE;
				break;
			default:
				switch (window.location.hostname) {
					case 'as.sgcetec.co.kr':
					case 'as2.sgcetec.co.kr':
					case 'as.sgcenc.co.kr':
					case 'as2.sgcenc.co.kr':
					case 'mas.sgcetec.co.kr':
					case 'mas2.sgcetec.co.kr':
					case 'mas.sgcenc.co.kr':
					case 'mas2.sgcenc.co.kr':
						this._appConf.appType = 'as';
						break;
					default:
						this._appConf.appType = 'portal';
						break;
				}
		}
		switch (window.location.hostname) {
			case 'localhost':
			case '192.168.36.22':
				this._appConf.showLogger = true;
				break;
			default:
				this._appConf.showLogger = false;
				break;
		}
		switch (this._appConf.appType) {
			case 'as':
				this._appConf.appName = 'As';
				break;
			case 'portal':
			default:
				this._appConf.appName = 'Portal';
				break;
		}
		this._appConf.addHomeScreen = async () => {
			if (this._appConf.deferredPrompt !== null) {
				switch (this._appConf.deferredPrompt) {
					case 'app':
					case 'standalone':
						this._commonService.openToast('LOGIN.MSG_ADDHOME_ADDED');
						break;
					case 'notready':
						{
							const userAgent = window?.navigator?.userAgent || '';
							if (userAgent.match(/SamsungBrowser/)) {
								this._commonService.openToast('LOGIN.MSG_ADDHOME_NOTREADY_SAMSUNG');
							} else if (userAgent.match(/iPhone/)) {
								this._commonService.openToast('LOGIN.MSG_ADDHOME_NOTREADY_IPHONE');
							} else {
								this._commonService.openToast('LOGIN.MSG_ADDHOME_NOTREADY');
							}
						}
						break;
					case 'pc':
						this._commonService.openToast('LOGIN.MSG_ADDHOME_UNABLE');
						break;
					default:
						await this._appConf.deferredPrompt.prompt();
						this._appConf.deferredPrompt.userChoice.then((choiceResult: any) => {
							if (choiceResult.outcome === 'accepted') {
								this._commonService.openToast('LOGIN.MSG_ADDHOME_ACCEPTED');
							} else {
								this._commonService.openToast('LOGIN.MSG_ADDHOME_UNACCEPTED');
							}
							this._appConf.deferredPrompt = null;
							this._installSubscriptions.next(this._appConf.deferredPrompt);
						});
						break;
				}
			}
		};
		const isStandalone = window.matchMedia('(display-mode: standalone)').matches;
		let installType = 'pc';
		if (document.referrer.startsWith('android-app://')) {
			installType = 'app';
		} else if (isStandalone || (window?.navigator as any)?.standalone) {
			installType = 'standalone';
		} else {
			if (
				window?.navigator?.userAgent.match(
					/iPhone|iPod|iPad|Android|Windows CE|BlackBerry|Symbian|Windows Phone|webOS|Opera Mini|Opera Mobi|POLARIS|IEMobile|lgtelecom|nokia|SonyEricsson/i
				) != null ||
				window?.navigator?.userAgent.match(/LG|SAMSUNG|Samsung/) != null
			) {
				installType = 'mobile';
			} else {
				installType = 'pc';
			}
		}
		this._appConf.installType = this._appConf.appType + '-' + installType;
	}

	/**
	 * Gets instance
	 *
	 * @returns instance
	 */
	public static getInstance(): SharedService {
		if (this._instance === null) {
			this._instance = new SharedService();
		}
		return this._instance;
	}

	/**
	 * 로그인 구독기
	 *
	 * @public
	 * @type {Subject}
	 */
	private _loginSubscriptions = new Subject<any>();

	/**
	 * 설치 구독기
	 *
	 * @public
	 * @type {Subject}
	 */
	private _installSubscriptions = new Subject<any>();

	/**
	 * Sets common service
	 *
	 * @param commonService
	 */
	public setCommonService(commonService: ICommonService) {
		this._commonService = commonService;
	}

	/**
	 * Subscribes shared service
	 *
	 * @param [subType]
	 * @returns subscribe
	 */
	public subscribe(subType: 'login' | 'install' = 'login'): Observable<any> {
		switch (subType) {
			case 'install':
				return this._installSubscriptions.asObservable();
			case 'login':
			default:
				return this._loginSubscriptions.asObservable();
		}
	}

	/**
	 * Sets route location
	 *
	 * @param routeLocation
	 */
	public setRouteLocation(routeLocation: RouteLocationNormalizedLoaded) {
		this._basePages.forEach((menu) => {
			menu._routeLocation = routeLocation;
		});
	}

	/**
	 * Prepares install
	 */
	public prepareInstall() {
		this.consoleLog('debug', 'referrer', '' + document.referrer);
		this.consoleLog('debug', 'installType', this._appConf.installType);
		switch (this._appConf.installType) {
			case 'as-pc':
			case 'portal-pc':
				this._appConf.deferredPrompt = 'pc';
				break;
			case 'as-standalone':
			case 'as-app':
			case 'portal-standalone':
			case 'portal-app':
				this._appConf.deferredPrompt = 'app';
				break;
			case 'as-mobile':
			case 'portal-mobile':
				this._appConf.deferredPrompt = 'notready';
				window.addEventListener('beforeinstallprompt', (e: any) => {
					e.preventDefault();
					this._appConf.deferredPrompt = e;
					this._installSubscriptions.next(this._appConf.deferredPrompt);
				});
				break;
		}
		this._installSubscriptions.next(this._appConf.deferredPrompt);
	}

	/**
	 * Gets app conf
	 *
	 * @returns app conf
	 */
	public getAppConf(): AppConfVo {
		return this._appConf;
	}

	/**
	 * Shared datas of shared service
	 */
	private _sharedDatas: { [key: string]: any } = {};

	/**
	 * Gets data
	 *
	 * @param key
	 * @returns data
	 */
	public getData(key: string): any {
		return this._sharedDatas[key] ?? null;
	}

	/**
	 * Sets data
	 *
	 * @param key
	 * @param value
	 */
	public setData(key: string, value: any) {
		if (value !== undefined && value !== null) {
			this._sharedDatas[key] = value;
		} else {
			this.unsetData(key);
		}
	}

	/**
	 * Unsets data
	 *
	 * @param key
	 */
	public unsetData(key: string) {
		if (this._sharedDatas[key] !== undefined) {
			delete this._sharedDatas[key];
		}
	}

	/**
	 * Base pages of shared service
	 */
	private _basePages: AppMenuVo[] = [
		{
			title: 'TIT_HOME',
			url: '/home',
			icon: Icon.homeOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_LOGIN',
			url: '/login',
			icon: Icon.logInOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_DEPTING',
			url: '/as/defting',
			icon: Icon.buildOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_DEPTDONE',
			url: '/as/deftdone',
			icon: Icon.documentTextOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_DEPTMANG',
			url: '/po/deftmang',
			icon: Icon.buildOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_DEPTQUE',
			url: '/po/deftque',
			icon: Icon.documentTextOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_ROOMREV',
			url: '/po/roomrev',
			icon: Icon.idCardOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_ROOMAPP',
			url: '/po/roomapp',
			icon: Icon.peopleOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_ADDRESS',
			url: '/address',
			icon: Icon.listOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_PUSHDEV',
			url: '/pushdev',
			icon: Icon.airplaneOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_TEST',
			url: '/test',
			icon: Icon.helpOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
		{
			title: 'TIT_SAMPLE',
			url: '/sample',
			icon: Icon.helpOutline,
			get selected() {
				return this._routeLocation && this._routeLocation.path.startsWith(this.url);
			},
		},
	];

	/**
	 * App pages of shared service
	 */
	private _appPages: AppMenuVo[] = [];

	/**
	 * Gets app menu
	 *
	 * @returns app menu
	 */
	public getAppMenu(): AppMenuVo[] {
		this.changeAppMenu();
		return this._appPages;
	}

	/**
	 * Changes app menu
	 */
	private changeAppMenu(): void {
		this._appPages = [];
		this._basePages.forEach((menu) => {
			if (this.isHasPageAuth(menu.url)) {
				this._appPages.push(menu);
			}
		});
	}

	/**
	 * Gets login info
	 *
	 * @returns login info
	 */
	public getLoginInfo(): UserDetailsVo {
		return this._appConf.loginInfo;
	}

	/**
	 * Sets login info
	 *
	 * @param info
	 */
	public setLoginInfo(info: UserDetailsVo) {
		Object.assign(this._appConf.loginInfo, info || { userId: null, userNm: null, covendId: null, isAsMember: false, authType: 'X' });
		this._appConf.loginInfo.connSiteCd = this._appConf.loginInfo.connSiteCd || '';
		this._appConf.authType = this._appConf.loginInfo.authSiteYn || 'X';
		if (this._appConf.loginInfo.userId) {
			if (this.isAsMember()) {
				this._appConf.authType = 'Z';
			} else {
				switch (this._appConf.authType) {
					case 'A':
					case 'C':
					case 'M':
						break;
					default:
						this._appConf.authType = 'X';
						break;
				}
			}
		} else {
			this._appConf.authType = 'R';
		}
		this._appConf.authMenu = [];
		if (this.isHasPageAuth('/as/defting')) {
			this._appConf.authMenu.push('defting');
		}
		if (this.isHasPageAuth('/as/deftdone')) {
			this._appConf.authMenu.push('deftdone');
		}
		if (this.isHasPageAuth('/po/deftmang')) {
			this._appConf.authMenu.push('deftmang');
		}
		if (this.isHasPageAuth('/po/deftque')) {
			this._appConf.authMenu.push('deftque');
		}
		if (this.isHasPageAuth('/po/roomrev')) {
			this._appConf.authMenu.push('roomrev');
		}
		if (this.isHasPageAuth('/po/roomapp')) {
			this._appConf.authMenu.push('roomapp');
		}
		if (this.isHasPageAuth('/address')) {
			this._appConf.authMenu.push('address');
		}

		this.changeAppMenu();
		this._loginSubscriptions.next(this._appConf.loginInfo);
		if (this._appConf.loginInfo?.userId !== null) {
			this.initFireBase();
		}
	}

	/**
	 * Determines whether null is
	 *
	 * @param input
	 * @returns true if null
	 */
	public isNull(input: any): boolean {
		if (typeof input === 'undefined' || input === null || input === '0' || input === 0) {
			return true;
		}
		if (typeof input === 'string') {
			if (input.split(' ').join('') === '') {
				return true;
			}
		}
		if (typeof input === 'number') {
			if (input === 0) {
				return true;
			}
		}
		if (Array.isArray(input)) {
			if (input.length === 0) {
				return true;
			}
		}
		return false;
	}

	/**
	 * Determines whether not null is
	 *
	 * @param input
	 * @returns true if not null
	 */
	public isNotNull(input: any): boolean {
		return !this.isNull(input);
	}

	/**
	 * Changes login data
	 *
	 * @param [siteCd]
	 * @param [pushUseYn]
	 * @param [pushKey]
	 */
	public changeLoginData(siteCd?: string, pushUseYn?: boolean, pushKey?: string) {
		const loginInfo = this._appConf.loginInfo;
		let changed = false;
		if (this.isNotNull(siteCd) && loginInfo.connSiteCd !== siteCd) {
			loginInfo.connSiteCd = siteCd;
			if (loginInfo.covendId) {
				changed = true;
			}
		}
		if (this.isNotNull(pushUseYn) && loginInfo.pushUseYn !== (pushUseYn ? 'Y' : 'N')) {
			loginInfo.pushUseYn = pushUseYn ? 'Y' : 'N';
			if (loginInfo.covendId) {
				changed = true;
			}
		}
		let changedPushKey = null;
		if (this.isNotNull(pushKey) && loginInfo.pushKey !== this._appConf.installType + '!-' + pushKey) {
			loginInfo.pushKey = this._appConf.installType + '-' + pushKey;
			changedPushKey = pushKey;
			changed = true;
		}
		if (changed) {
			this._api
				.post('api/info', {
					connSiteCd: loginInfo.connSiteCd,
					pushUseYn: loginInfo.pushUseYn,
					pushKey: changedPushKey,
					domainId: this._appConf.installType,
				})
				.then((result) => {
					this.consoleLog('debug', 'Info Changed', result);
				});
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Logger  of shared service
	 */
	private _logger: ILogerService = null;

	/**
	 * Sets logger
	 *
	 * @param logger
	 */
	public setLogger(logger: ILogerService) {
		this._logger = logger;
	}

	/**
	 * Consoles log
	 *
	 * @param title
	 * @param obj
	 */
	public consoleLog(type: ConsoleLogType, title: string, ...obj: any[]) {
		if (this._logger !== null) {
			this._logger.consoleLog(type, title, ...obj);
		}
	}

	private _firebaseApp: FirebaseApp = null;
	private _firebaseMessage: Messaging = null;

	/**
	 * Inits fire base
	 * 어플로 설치가 된 경우만 구독이 가능 하도록 함.
	 */
	private async initFireBase() {
		if (navigator.serviceWorker) {
			switch (this._appConf.installType) {
				case 'as-standalone':
				case 'as-app':
				case 'portal-standalone':
				case 'portal-app':
					if (this._firebaseApp === null) {
						const firebaseConfig = {
							apiKey: this._appConf.processEnv.VUE_APP_API_KEY,
							authDomain: this._appConf.processEnv.VUE_APP_AUTH_DOMAIN,
							projectId: this._appConf.processEnv.VUE_APP_PROJECT_ID,
							storageBucket: this._appConf.processEnv.VUE_APP_STORAGE_BUCKET,
							messagingSenderId: this._appConf.processEnv.VUE_APP_MESSAGING_SENDER_ID,
							appId: this._appConf.processEnv.VUE_APP_ID,
							measurementId: this._appConf.processEnv.VUE_APP_MEASUREMENT_ID,
						};
						this._firebaseApp = initializeApp(firebaseConfig);
					}
					if (this._firebaseApp !== null) {
						this.initFirebaseMessageToken();
					}
					break;
			}
		} else {
			this.consoleLog('debug', 'ServiceWorker', 'not ready');
		}
	}

	/**
	 * Inits firebase message token
	 */
	public initFirebaseMessageToken() {
		if (this._firebaseApp !== null && this._firebaseMessage === null) {
			this._firebaseMessage = getMessaging(this._firebaseApp);
			onMessage(this._firebaseMessage, (payload) => {
				const payloadNotification: any = payload.notification || {};
				const payloadData = payload.data || {};
				this._commonService.openToast(
					payloadNotification.body || payloadData.body,
					null,
					null,
					payloadNotification.title,
					'primary',
					Icon.airplaneOutline,
					null,
					payloadNotification.click_action || payloadData.click_action || payloadNotification.clickAction || payloadData.clickAction
				);
				this.consoleLog('debug', 'onMessage', payload);
			});
		}
		if (this._firebaseMessage !== null && Notification) {
			Notification.requestPermission().then((permission) => {
				if (permission === 'granted') {
					getToken(this._firebaseMessage, {
						vapidKey: this._appConf.processEnv.VUE_APP_VAPID_KEY,
					})
						.then((currentToken) => {
							if (currentToken) {
								this.changeLoginData(null, null, currentToken);
							}
						})
						.catch((err) => {
							this.consoleLog('debug', 'Messaging Error', err);
						});
				} else {
					this._commonService.openToast('LOGIN.MSG_PUSH_PERMISSION');
				}
			});
		}
	}

	/**
	 * Refreshs firebase message token
	 */
	public refreshFirebaseMessageToken() {
		if (this._firebaseMessage !== null) {
			deleteToken(this._firebaseMessage)
				.then((value) => {
					if (value) {
						this.initFirebaseMessageToken();
					}
				})
				.catch((ex) => {
					this.consoleLog('error', 'deleteToken Error', ex);
				});
		}
	}

	/**
	 * Checks logout
	 *
	 * @returns
	 */
	public async checkLogout() {
		return this._api
			.get('api/logout')
			.then(() => {
				this._loginchecked = false;
				this.setLoginInfo({ userId: null, userNm: null });
			})
			.catch(() => {
				this._loginchecked = false;
				this.setLoginInfo({ userId: null, userNm: null });
			});
	}

	/**
	 * Checks login
	 *
	 * @returns
	 */
	public async checkLogin() {
		this._loginchecked = false;
		return this.isLogined('/home').then((result) => {
			return result === '/home' ? true : false;
		});
	}

	/**
	 * Determines whether as member is
	 *
	 * @returns true if as member
	 */
	public isAsMember(): boolean {
		if (this._appConf.loginInfo.userId && this._appConf.loginInfo.covendId) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * Loginchecked  of shared service
	 */
	private _loginchecked = false;

	/**
	 * Determines whether logined is
	 *
	 * @returns logined
	 */
	public async isLogined(routerPath: string): Promise<string> {
		return new Promise<string>((resolve) => {
			if (!this._loginchecked) {
				this._loginchecked = true;
				this._api
					.get('api/info')
					.then((info) => {
						this.setLoginInfo(info);
						if (this._appConf.loginInfo?.userId !== null) {
							resolve(this.isHasPageAuth(routerPath) ? routerPath : '/home');
						} else {
							resolve('/login');
						}
					})
					.catch((error) => {
						switch (error?.message) {
							case 'exception.session.invalid':
								this._commonService.openToast('LOGIN.MSG_SESSION_INVALID', null, null, 'LOGIN.TIT_LOGOUT', 'warning');
								break;
							default:
								break;
						}
						resolve('/login');
					});
			} else {
				if (this._appConf.loginInfo?.userId !== null) {
					if (['M', 'A', 'C', 'Z'].includes(this._appConf.loginInfo.authSiteYn)) {
						resolve(this.isHasPageAuth(routerPath) ? routerPath : '/home');
					} else {
						resolve('/login');
					}
				} else {
					resolve('/login');
				}
			}
		});
	}

	/**
	 * Determines whether has page auth is
	 *
	 * @param routerPath
	 * @returns true if has page auth
	 */
	public isHasPageAuth(routerPath: string): boolean {
		if (this._appConf.loginInfo?.userId !== null) {
			const authType = this._appConf.authType;
			if (routerPath.split('/').length >= 4) {
				routerPath = routerPath.substring(0, routerPath.lastIndexOf('/'));
			}
			switch (routerPath) {
				case '/home':
					return true;
				case '/as/defting':
				case '/as/deftdone':
					return ['Z'].includes(authType);
				case '/po/deftmang':
					return ['A'].includes(authType);
				case '/po/deftque':
					return ['A'].includes(authType);
				case '/po/roomapp':
					return ['Q'].includes(authType); // 사용안함
				case '/po/roomrev':
					return ['M', 'A', 'C'].includes(authType);
				case '/address':
					return ['M', 'A'].includes(authType);
				case '/error':
					return true;
				case '/pushdev':
				case '/test':
				case '/sample':
					return this._appConf.processEnv.NODE_ENV === 'development';
				default:
					return false;
			}
		} else {
			switch (routerPath) {
				case '/error':
				case '/login':
					return true;
				case '/test':
				case '/sample':
					return this._appConf.processEnv.NODE_ENV === 'development';
				default:
					return false;
			}
		}
	}
}

export default SharedService.getInstance();
