import { Injectable } from '@angular/core';
import { HttpClient, HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { CanActivate, Router } from '@angular/router';

import { Observable } from 'rxjs';
import { tap, shareReplay, combineAll } from 'rxjs/operators';

import jwtDecode from 'jwt-decode';
import * as moment from 'moment';

import { environment } from '../../environments/environment';

@Injectable({
  providedIn: 'root'
})

export class AuthService {

  private apiRoot = environment.apiroot +'auth/';

  constructor(private http: HttpClient, private router:Router) { }

  private setSession(authResult: any) {
    const token = authResult.token;
    const payload = <JWTPayload> jwtDecode(token);
    const expiresAt = moment.unix(payload.exp);

    localStorage.setItem('token', authResult.token);
    localStorage.setItem('expires_at', JSON.stringify(expiresAt.valueOf()));
  }

  get_token(): string {
    return JSON.parse(localStorage.getItem('currentUser') || '{}');
  }

  login(email: string, password: string) {
    return this.http.post(
      this.apiRoot.concat('login/'),
      { email, password }
    ).pipe(
      tap(response => this.setSession(response)), 
      //tap(response => console.log(response)),

      shareReplay(),
    );
  }

  signup(username: string, email: string, password1: string, password2: string) {
    // TODO: implement signup
  }

  logout() {
    const token = localStorage.getItem('token');
    this.http.post(
      this.apiRoot.concat('logout/'),
      { token: token }
    );
    localStorage.removeItem('token');
    localStorage.removeItem('expires_at');
    this.router.navigate(['/login']);
  }

  refreshToken() {
    const token = localStorage.getItem('token');
    if (moment().isBetween(this.getExpiration().subtract(1, 'days'), this.getExpiration())) {
      return this.http.post(
        this.apiRoot.concat('refresh-token/'),
        { token: token }
      ).pipe(
        tap(response => this.setSession(response)),
        shareReplay(),
      ).subscribe();
    }
    return
  }

  getExpiration() {
    try {
      const expiration = JSON.parse(localStorage.getItem('expires_at') || '{}');
      const expiresAt = JSON.parse(expiration);

      return moment(expiresAt);
    } catch(e){
      return moment(0);}
  }

  isLoggedIn() {
      return moment().isBefore(this.getExpiration());
    }

  isLoggedOut() {
    return !this.isLoggedIn();
  }
}

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('token');

    if (token) {
      const cloned = req.clone({
        headers: req.headers.set('Authorization', 'Bearer '.concat(token))
      });

      return next.handle(cloned);
    } else {
      return next.handle(req);
    }
  }
}

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) { }

  canActivate() {
    if (this.authService.isLoggedIn()) {
      this.authService.refreshToken();

      return true;
    } else {
      this.authService.logout();
      this.router.navigate(['login']);

      return false;
    }
  }
}

interface JWTPayload {
  user_id: number;
  username: string;
  email: string;
  exp: number;
}