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

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

import { Headers, Http, RequestOptions, Response } from '@angular/http';

import { Observable } from 'rxjs/Observable';

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/toPromise';

export interface IToken {
  token: string;
  expiry: number;
  isLogin?: boolean;
}

export interface IOperatorCredentials {
  operatorName: string;
  operatorPassword: string;
}

const serverApiUrl = environment.serverApiUrl;
const API_IDENTIFIER = environment.apiIdentifier;
const TOKEN_COOKIE_NAME = 'ldi-operator-token';
const TOKEN_API_URL = `${serverApiUrl}/token/${API_IDENTIFIER}`;

@Injectable()
export class TokenService {
  get authorizationToken(): IToken {
    const cookieToken = TokenService.GetFromCookies(TOKEN_COOKIE_NAME);
    if (cookieToken) {
      return JSON.parse(cookieToken);
    }
    return null;
  }
  get isLogin(): boolean {
    return this.authorizationToken && this.authorizationToken.isLogin;
  }
  private static GetFromCookies(cookieName: string = null): any {
    if (cookieName != null) {
      return decodeURIComponent(<string>document
        .cookie.match(new RegExp(`(?:(?:^|.*;\\s*)${cookieName}\\s*\\=\\s*([^;]*).*$)|^.*$`))[1] || '');
    }
    return document.cookie.split(';').reduce((acc, cur) => {
      const values = cur.trim().split('=');
      acc[values[0]] = values[1];
      return acc;
    }, {});
  }
  constructor(private http: Http, private router: Router) { }

  get authorization(): string {
    return this.authorizationToken && `Bearer ${this.authorizationToken.token}` || null;
  }

  get authorizationHeaders(): Headers {
    return new Headers({ Authorization: this.authorization });
  }

  apiConsumerToken(): Promise<IToken> {
    if (this.authorizationToken) {
      return Promise.resolve(this.authorizationToken);
    }

    let headers: Headers = new Headers({ 'Content-Type': 'application/json' });
    let requestOptions: RequestOptions = new RequestOptions({ headers });

    return this.http.get(TOKEN_API_URL, requestOptions)
      .map((response: Response) => {
        let data: IApiResponseError | IApiResponseSuccess<IToken> = response.json();
        if (isApiResponseError(data)) {
          throw data.error;
          // return Observable.throw(data.error);
        }
        this.SaveToCookies(data.result);
        return data.result;
      }).catch(e => this.handleError(e)).toPromise();
  }

  apiOperatorToken(userCredentials: IOperatorCredentials): Promise<IToken> {
    let headers: Headers = new Headers({ 'Content-Type': 'application/json' });
    let requestOptions: RequestOptions = new RequestOptions({ headers });
    return this.http.post(`${TOKEN_API_URL}/operator`,
      userCredentials || { userName: null, userPassword: null }, requestOptions)
      .map((response: Response) => {
        let data: IApiResponseError | IApiResponseSuccess<IToken> = response.json();
        if (isApiResponseError(data)) {
          throw data.error;
        }
        data.result.isLogin = true;
        this.SaveToCookies(data.result);
        return data.result;
      }).catch(e => this.handleError(e)).toPromise();
  }

  removeToken(): void {
    this.DeleteFromCookies(TOKEN_COOKIE_NAME);
  }
  private DeleteFromCookies(cookieName: string): void {
    const expiryDate: Date = new Date(0);
    document.cookie = `${cookieName}='';path=/;expires=${expiryDate.toUTCString()}`;
  }
  private SaveToCookies(token: IToken): void {
    // Cookie.set(TOKEN_COOKIE_NAME, JSON.stringify(token), token.expiry / 60 / 60 / 24, '/');
    const expiry: Date = new Date(Date.now() + token.expiry * 1000 / 2);
    document.cookie = `${TOKEN_COOKIE_NAME}=${encodeURIComponent(JSON.stringify(token))};path=/;expires=${expiry.toUTCString()}`;
  }
  /**
  * Handle HTTP error
  */
  private handleError(error: any) {
    if (error.status === 401) {
      this.router.navigate(['/session', 'signin']);
    }

    let errMsg = (error.message) ? error.message :
      error.status ? `${error.status} - ${error.statusText}` : 'Server error';
    console.error(errMsg); // log to console instead
    return Observable.throw(errMsg);
  }
}
export interface IApiResponseSuccess<T> {
  result: T;
}
export interface IApiResponseError {
  error: IApiError;
}
export interface IApiError {
  id: number;
  msg: string;
}
export function isApiResponseError<T>(data: IApiResponseError | IApiResponseSuccess<T>): data is IApiResponseError {
  return (<IApiResponseError>data).error != null;
}
