import { Config, Inject, Provide } from '@midwayjs/core';
import { get } from 'lodash';
import { RoleService } from '../service/system/role.service';
import { RedisService } from '@midwayjs/redis';
import * as Crypto from 'crypto-js';
import { Context } from '@midwayjs/koa';
import { ServiceError, ErrorCode } from '../error/service.error';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Equal, Repository } from 'typeorm';
import { LoginDTO, LoginType, UPwdDTO } from '../interface/login.interface';
import { Opera } from '../frame/dbOpera';
import { Admin } from '../entity/system/admin.entity';
import * as bcrypt from 'bcryptjs';
import { User } from '../entity/system/user.entity';

@Provide()
export class LoginService {
  @Inject()
  roleService: RoleService;
  @Inject()
  ctx: Context;
  @Config('loginSign')
  loginSign;
  @Config('jwt.secret')
  jwtSecret;
  @Config('jwt.expiresIn')
  jwtExpiresIn;
  @Inject()
  redisService: RedisService;

  @InjectEntityModel(Admin)
  adminModel: Repository<Admin>;
  @InjectEntityModel(User)
  userModel: Repository<User>;

  async onePointLogin(loginVo) {
    const { id, role } = loginVo;
    const rediskey = `${this.loginSign}:${role}:${id}`;
    // 随机字符串 验证用户登录用
    const str = this.randomStr();
    // 拼接成token内的登录code 加密前数据
    const val = `${rediskey}:${str}`;
    // 加密后存入token内
    const code = Crypto.AES.encrypt(val, this.jwtSecret).toString();
    // 设置redis登录记录 将 随机字符串存在redis中. 验证时需要将加密的字符串解密,解密后和redis中的对比验证
    await this.redisService.set(rediskey, str, 'EX', this.jwtExpiresIn);
    return code;
  }

  /**
   * 账密登录
   * @param data 用户名和密码
   * @param type 用户类型
   * @returns 用户信息/空值
   */
  async loginByAccount(data: LoginDTO, type: LoginType) {
    let model;
    if (type === LoginType.Admin) model = this.adminModel;
    else model = this.userModel;
    const user = await model.createQueryBuilder('t').where('t.account = :account', { account: data.account }).addSelect('t.password').getOne();
    if (!user) throw new ServiceError(ErrorCode.USER_NOT_FOUND);
    await this.checkAccountCanLogin(user, type);
    const result = bcrypt.compareSync(data.password, user.password);
    if (!result) throw new ServiceError(ErrorCode.BAD_PASSWORD);
    return user;
  }
  /**
   * 检查用户是否可以登录(从用户本身和角色检查)
   * @param user 用户信息
   * @param type 用户类型
   */
  async checkAccountCanLogin(user, type: LoginType) {
    // 判断是否可以使用:
    // 管理员: 超级管理员无视,直接往下; 普通管理员需要查看is_use是不是'0'
    // 其他用户需要看status是不是'1';
    if (type === 'Admin') {
      if (get(user, 'is_super') === '1') {
        if (get(user, 'is_use') === '1') throw new ServiceError(ErrorCode.USER_IS_DISABLED);
      }
    } else {
      if (get(user, 'status') !== '1') throw new ServiceError(ErrorCode.USER_IS_DISABLED);
      const qb = [
        { column: 'code', opera: [Opera.Equal], value: type },
        { column: 'is_use', opera: [Opera.Equal], value: '0' },
      ];
      const role = await this.roleService.fetch(qb);
      if (!role) throw new ServiceError(ErrorCode.ROLE_IS_DISABLED);
    }
  }
  // 修改密码
  async updatePwd(data: UPwdDTO, type: LoginType) {
    let model;
    if (type === LoginType.Admin) model = this.adminModel;
    else model = this.userModel;
    const user = await model.findOne({ where: { id: Equal(data.id) } });
    if (!user) new ServiceError(ErrorCode.USER_NOT_FOUND);
    await model.update({ id: data.id }, { password: data.password });
  }

  // 需要改的时候再用
  async wxAppLogin(openid: string, type: LoginType) {
    let model;
    if (type === LoginType.Admin) model = this.adminModel;
    else model = this.userModel;
    const user = await model.findOne({ where: { openid: Equal(openid) } });
    if (!user) throw new ServiceError(ErrorCode.USER_NOT_FOUND);
    return user;
  }

  randomStr(len = 32) {
    return Math.random().toString(36).slice(-len);
  }
}
