
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { Credential } from '@app/core/model/auth/credential';
import { LoginInfo } from '@app/core/model/auth/login-info';
import { Principal } from '@app/core/model/auth/principal';
import { ConfigurableProvider } from '@app/core/model/configurable-provider/configurableProvider';
import { SnackBarNotificationLevel, UiNotificationService } from '@app/core/services/notification/ui-notification-service';
import { BaseService } from '@common/base/base.service';
import { TranslateService } from '@ngx-translate/core';
import { environment } from 'environments/environment';
import { Observable, of as observableOf, throwError as observableThrowError } from 'rxjs';
import { catchError, map, takeUntil } from 'rxjs/operators';
import { ConfigurationService } from '../configuration/configuration.service';
import { CookieService } from 'ngx-cookie-service';

@Injectable()
export class AuthService extends BaseService {
	private actionUrl: string;
	private headers: HttpHeaders;

	constructor(
		private http: HttpClient,
		private snackBar: MatSnackBar,
		private language: TranslateService,
		private router: Router,
		private uiNotificationService: UiNotificationService,
		private configurationService: ConfigurationService,
		private cookieService: CookieService
	) {
		super();


		this.headers = new HttpHeaders();
		this.headers = this.headers.set('Content-Type', 'application/json');
		this.headers = this.headers.set('Accept', 'application/json');
	}

	private clear(): void {
		localStorage.removeItem('principal');
	}

	public current(principal?: Principal): Principal {
		if (principal) {
			localStorage.setItem('principal', JSON.stringify(principal));
			return principal;
		}
		const principalJson = localStorage.getItem('principal');
		if (principalJson === null || principalJson === undefined) {
			return null;
		 }
		let principalObj = JSON.parse(principalJson) as Principal;
		principalObj.expiresAt = new Date(principalObj.expiresAt);
		if (principalObj.expiresAt < new Date()) {
			return null;
		}
		return principalObj;
	}

	//public login(credential: Credential): Observable<Principal> {
	//    const url = this.actionUrl + 'login';

	//    return this.http.post(url, credential, { headers: this.headers })
	//        .map((res: Response) => {
	//            let principal = this.current(new JsonSerializer<Principal>().fromJSONObject(res, Principal));
	//            //this.loginContextSubject.next(true);
	//            return principal;
	//        })
	//        .catch((error: any) => {
	//            //this.loginContextSubject.next(false);
	//            return Observable.throw(error);
	//        });
	//}

	public login(loginInfo: LoginInfo): Observable<Principal> {
		this.actionUrl = this.configurationService.server + 'auth/';
		const url = this.actionUrl + 'externallogin';

		return this.http.post(url, loginInfo, { headers: this.headers }).pipe(
			map((res: any) => {
				const principal = this.current(res.payload);
				// this.cookieService.set('cookiesConsent', 'true', 356);
				this.cookieService.set("cookiesConsent", "true", 356,null,null,false, 'Lax');
				//this.loginContextSubject.next(true);
				return principal;
			}),
			catchError((error: any) => {
				//this.loginContextSubject.next(false);
				return observableThrowError(error);
			}));
	}

	public mergeLogin(loginInfo: LoginInfo): Observable<any> {
		this.actionUrl = this.configurationService.server + 'auth/';
		const url = this.actionUrl + 'externallogin';

		return this.http.post(url, loginInfo, { headers: this.headers });
	}

	public nativeLogin(credentials: Credential): Observable<Principal> {
		this.actionUrl = this.configurationService.server + 'auth/';
		const url = this.actionUrl + 'nativelogin';

		return this.http.post(url, credentials, { headers: this.headers }).pipe(
			map((res: any) => {
				const principal = this.current(res.payload);
				// this.cookieService.set('cookiesConsent', 'true', 356);
				this.cookieService.set("cookiesConsent", "true", 356,null,null,false, 'Lax');
				//this.loginContextSubject.next(true);
				return principal;
			}),
			catchError((error: any) => {
				//this.loginContextSubject.next(false);
				return observableThrowError(error);
			}));
	}


	public logout(): void {
		this.actionUrl = this.configurationService.server + 'auth/';
		const url = this.actionUrl + 'logout';
		const principal = this.current();
		this.clear();

		if (!principal) { return; }
		let headers = this.headers;
		headers = headers.set('AuthToken', principal.token);
		this.http.post(url, null, { headers: headers })
			.pipe(takeUntil(this._destroyed))
			.subscribe(
				res => this.onLogOutSuccess(res),
				error => this.onLogOutError(error)
			);
	}

	public me(): Observable<Principal> {
		this.actionUrl = this.configurationService.server + 'auth/';
		const url = this.actionUrl + 'me';
		const principal = this.current();
		if (!principal) {
			this.clear();
			return observableOf<Principal>();
		}
		let headers = this.headers;
		headers = headers.set('AuthToken', principal.token);
		return this.http.post(url, null, { headers: headers }).pipe(
			map((res: any) => {
				const princ = this.current(res.payload);
				princ.expiresAt = new Date(princ.expiresAt);
				console.log("Token Expires at: " + princ.expiresAt.toDateString() + ' ' + princ.expiresAt.toLocaleTimeString());
				return princ;
			}),
			catchError((error: any) => {
				//console.warn('could not retrieve me info:\n', error);
				this.clear();
				const princ = this.current();
				this.router.navigate(['/login']);
				return observableOf<Principal>(princ);
			}));
	}

	public onLogOutSuccess(logoutMessage: any) {
		this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.SUCCESSFUL-LOGOUT'), SnackBarNotificationLevel.Success);
		this.router.navigate(['/login']);
	}

	public onLogOutError(errorMessage: string) {
		this.uiNotificationService.snackBarNotification(this.language.instant('GENERAL.SNACK-BAR.UNSUCCESSFUL-LOGOUT'), SnackBarNotificationLevel.Error);
		this.router.navigate(['/login']);
	}

	public getConfigurableProviders(): Observable<ConfigurableProvider[]> {
		this.actionUrl = this.configurationService.server + 'auth/';
		const url = this.actionUrl + 'configurableLogin';
		return this.http.get<ConfigurableProvider[]>(url, { headers: this.headers }).pipe(
			map((res: any) => {
				const providers = res.payload.providers;
				return providers;
			})
		);
	}

	public getUserFromToken(token: string): Observable<Principal> {
		this.actionUrl = this.configurationService.server + 'auth/';
		const url = this.actionUrl + 'me';
		let headers = this.headers;
		headers = headers.set('AuthToken', token);
		return this.http.post(url, null, { headers: headers }).pipe(
			map((res: any) => {
				const princ = this.current(res.payload);
				princ.expiresAt = new Date(princ.expiresAt);
				return princ;
			}),
			catchError((error: any) => {
				this.clear();
				const princ = this.current();
				this.router.navigate(['/login']);
				return observableOf<Principal>(princ);
			}));
	}
}
