import { isNull } from 'lodash';

import { useStorage } from '@react-devui/hooks';

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

export const TOKEN_KEY = 'token';
export const TOKEN_TYPE: 'JWT' | 'CUSTOM' = 'JWT';
export const TOKEN_EXPIRATION_OFFSET = 10 * 1000;
export const TOKEN_REFRESH = true;
export const TOKEN_REFRESH_OFFSET = 60 * 1000;

if (!environment.production) {
  if (TOKEN_REFRESH && TOKEN_REFRESH_OFFSET <= TOKEN_EXPIRATION_OFFSET) {
    console.warn('`TOKEN_REFRESH_OFFSET` should be greater than `TOKEN_EXPIRATION_OFFSET`');
  }
}

export abstract class Token {
  public abstract get expiration(): number | null;
  public abstract set expiration(val: number | null);

  public get value(): string | null {
    return useStorage.SERVICE.getItem(TOKEN_KEY);
  }

  public get expired(): boolean {
    if (isNull(this.expiration)) {
      return false;
    } else {
      return this.expiration - TOKEN_EXPIRATION_OFFSET <= Date.now();
    }
  }

  set(val: string | null) {
    useStorage.SERVICE.setItem(TOKEN_KEY, val);
  }
  remove() {
    useStorage.SERVICE.removeItem(TOKEN_KEY);
  }
}

export interface JWTTokenPayload {
  iss: string;
  sub: string;
  aud: string;
  exp: number;
  nbf: number;
  iat: number;
  jti: string;
}
export class JWTToken<T extends JWTTokenPayload> extends Token {
  public get payload(): T | null {
    if (isNull(this.value)) {
      return null;
    }
    const [, payload] = this.value.split('.');
    return JSON.parse(base64url.decode(payload));
  }
  public get expiration(): number | null {
    return isNull(this.payload) ? null : this.payload.exp * 1000;
  }
  public set expiration(val: number | null) {
    throw new Error('You should not change `expiration` when use JWT!');
  }
}

export class CustomToken extends Token {
  public expiration: number | null = null;
}

export const TOKEN = TOKEN_TYPE === 'JWT' ? new JWTToken() : new CustomToken();