import { CacheConstants, CookieConstants } from '../../constants';
import { AuthenticationUser } from '../../entities';
import { NetService } from '../../service';
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { JwtService } from '@nestjs/jwt';
import { AxiosRequestHeaders } from 'axios';
import { FastifyReply, FastifyRequest } from 'fastify';
import { isArray, isNilOrEmpty, notNilEmpty } from 'qx-util';
import { CachingService } from '../caching/caching.service';

/**
 * 认证服务
 *
 * @author chitanda
 * @date 2021-08-17 20:08:13
 * @export
 * @class AuthService
 */
@Injectable()
export class AuthService {
  /**
   * 请求服务
   *
   * @author chitanda
   * @date 2021-08-26 10:08:04
   * @private
   * @type {NetService}
   */
  private http: NetService = new NetService('loginv7');
  private uaaHttp: NetService = new NetService('uaa');

  /**
   * 是否启用 redis
   *
   * @author chitanda
   * @date 2021-11-03 22:11:03
   * @protected
   */
  protected enableRedis = false;

  /**
   * Creates an instance of AuthService.
   *
   * @author chitanda
   * @date 2021-08-26 10:08:13
   * @param {CachingService} cache
   */
  constructor(private readonly cache: CachingService, private readonly jwtService: JwtService, private readonly configService: ConfigService) {
    const enable = this.configService.get('redis.enable');
    if (enable == true || enable == 'true') {
      this.enableRedis = true;
    }
  }

  /**
   * 登录
   *
   * @author chitanda
   * @date 2021-09-03 16:09:02
   * @param {FastifyRequest} request
   * @param {FastifyReply} response
   * @param {string} loginname
   * @param {string} password
   * @return {*} 
   */
  async login(request: FastifyRequest, response: FastifyReply, loginname: string, password: string) {
    const res = await this.http.post(request, response, '/v7/login', { loginname, password });
    if (res) {
      const authToken = res.data.token;
      const data = this.jwtService.decode(authToken);
      let userName = '';
      if (typeof data === 'string') {
        userName = data;
      } else {
        userName = data.sub;
      }
      response.setCookie(CookieConstants.USER_NAME, userName, { path: '/' });
    }
    return res.data;
  }

  /**
   * 登出
   *
   * @author chitanda
   * @date 2021-09-03 16:09:14
   * @param {FastifyRequest} request
   * @param {FastifyReply} response
   * @return {*} 
   */
  async logout(request: FastifyRequest, response: FastifyReply) {
    const username = request.cookies.username;
    const res = await this.http.get(request, response, '/v7/logout');
    const cacheKey = this.getUserCacheKey(request, username);
    await this.cache.del(cacheKey);
    response.clearCookie(CookieConstants.USER_NAME);
    return res.data;
  }

  /**
   * 刷新认证标识
   *
   * @author chitanda
   * @date 2021-09-28 11:09:37
   * @param {FastifyRequest} request
   * @param {FastifyReply} response
   * @return {*} 
   */
  async refreshToken(request: FastifyRequest, response: FastifyReply) {
    const res = await this.http.get(request, response, '/v7/refreshToken');
    return res.data;
  }

  /**
   * 根据用户名称获取用户信息
   *
   * @author chitanda
   * @date 2021-08-17 21:08:25
   * @param {FastifyRequest} request
   * @param {string} srfsystemid
   * @param {string} srforgid
   * @param {string} username
   */
  async loginByUsername(
    request: FastifyRequest,
    response: FastifyReply,
    username: string,
  ): Promise<AuthenticationUser> {
    const cacheKey = this.getUserCacheKey(request, username);
    let user = await this.cache.get(cacheKey);
    if (isNilOrEmpty(user)) {
      const headers: AxiosRequestHeaders = { 'content-type': 'text/plain', 'authorization': request.headers.authorization };
      if (notNilEmpty(request.headers.srfsystemid)) {
        headers.srfsystemid = request.headers.srfsystemid as string;
      }
      if (notNilEmpty(request.headers.srforgid)) {
        headers.srforgid = request.headers.srforgid as string;
      }
      const res = await this.uaaHttp.post2(request, response, '/uaa/loginbyusername', username, { headers });
      user = res.data;
      await this.cache.set(cacheKey, user);
    } else {
      if (this.enableRedis === true) {
        user = this.formatUserData(user);
      }
    }
    return new AuthenticationUser(user);
  }

  /**
   * 获取用户缓存标识
   *
   * @author chitanda
   * @date 2021-11-03 22:11:24
   * @protected
   * @param {FastifyRequest} request
   * @param {string} username
   * @return {*} 
   */
  protected getUserCacheKey(request: FastifyRequest, username: string) {
    const { srfsystemid, srforgid } = request.headers;
    let cacheKey = `getByUsername:${username}`;
    if (notNilEmpty(srfsystemid) && notNilEmpty(srforgid)) {
      cacheKey = `${srfsystemid}:${srforgid}:${cacheKey}`;
    }
    return `${CacheConstants.userCache}:${cacheKey}`;
  }

  /**
   * 测试当前用户对于应用资源的权限
   *
   * @author chitanda
   * @date 2021-10-19 09:10:40
   * @param {FastifyRequest} request
   * @param {FastifyReply} response
   * @param {string[]} uniresKeys
   */
  async testSysUniRes(request: FastifyRequest,
    response: FastifyReply, uniresKeys: string[]): Promise<any[]> {
    if (notNilEmpty(uniresKeys)) {
      const res = await this.uaaHttp.post(request, response, '/uaa/testsysunires', uniresKeys, {
        headers: { 'Content-Type': 'application/json; charset=utf-8' },
      });
      if (res) {
        const items: boolean[] = res.data;
        if (isNilOrEmpty(items) || items.length !== uniresKeys.length) {
          throw new Error('当前用户权限标识计算异常，数量不匹配');
        }
        const results: string[] = [];
        for (let i = 0; i < uniresKeys.length; i++) {
          const str = uniresKeys[i];
          const bol = items[i];
          if (bol === true) {
            results.push(str);
          }
        }
        return results;
      }
    }
    return [];
  }

  /**
   * 临时反序列化redis中的缓存数据
   *
   * @author chitanda
   * @date 2021-08-23 11:08:46
   * @protected
   * @deprecated
   * @param {*} user
   */
  protected formatUserData(user: any): any {
    if (user && isArray(user) && user.length >= 2) {
      const type = user[0];
      const data = user[1];
      switch (type) {
        case 'cn.ibizlab.util.security.AuthenticationUser':
        case 'java.util.LinkedHashMap':
        case 'com.alibaba.fastjson.JSONObject':
          return this.parseObj(data);
        case 'java.util.ArrayList':
        case 'java.util.HashSet':
          return this.parseArr(data);
      }
    }
  }

  /**
   * 反序列化java的对象。
   *
   * @author chitanda
   * @date 2021-08-26 10:08:47
   * @protected
   * @deprecated
   * @param {Record<string, any>} data
   * @return {*}  {Record<string, any>}
   */
  protected parseObj(data: Record<string, any>): Record<string, any> {
    const _data: any = {};
    const keys = Object.keys(data);
    keys.forEach(key => {
      const value = data[key];
      if (isArray(value)) {
        const resultData = this.formatUserData(value);
        _data[key] = resultData;
      } else {
        _data[key] = value;
      }
    });
    return _data;
  }

  /**
   * 反序列化java数组型
   *
   * @author chitanda
   * @date 2021-08-26 10:08:27
   * @protected
   * @deprecated
   * @param {any[]} items
   * @return {*}  {any[]}
   */
  protected parseArr(items: any[]): any[] {
    const _items: any[] = [];
    if (notNilEmpty(items)) {
      items.forEach(item => {
        if (isArray(item)) {
          _items.push(this.formatUserData(item));
        } else {
          _items.push(item);
        }
      });
    }
    return _items;
  }
}
