import { Inject, Provide } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Equal, Repository } from 'typeorm';
import { Role } from '../../entity/system/role.entity';
import { Context } from '@midwayjs/koa';
import { AdminService } from './admin.service';
import { compact, flattenDeep, get, last, lowerFirst, uniq, upperFirst } from 'lodash';
import { DeptService } from './dept.service';
import { MenusService } from './menus.service';
import { BaseServiceV2 } from '../../frame/BaseServiceV2';
import { UserMenusService } from './userMenus.service';
import { Investment } from '../../entity/users/investment.entity';
import { State } from '../../entity/users/state.entity';
import { Association } from '../../entity/users/association.entity';
import { Competition } from '../../entity/users/competition.entity';
import { Incubator } from '../../entity/users/incubator.entity';
import { Company } from '../../entity/users/company.entity';
import { Unit } from '../../entity/users/unit.entity';
import { Expert } from '../../entity/users/expert.entity';
import { School } from '../../entity/users/school.entity';
@Provide()
export class RoleService extends BaseServiceV2 {
  @InjectEntityModel(Role)
  model: Repository<Role>;

  @Inject()
  deptService: DeptService;

  @Inject()
  adminService: AdminService;

  @Inject()
  menusService: MenusService;

  @Inject()
  userMenusService: UserMenusService;

  @InjectEntityModel(Investment)
  Investment: Repository<Investment>;
  @InjectEntityModel(State)
  State: Repository<State>;
  @InjectEntityModel(Association)
  Association: Repository<Association>;
  @InjectEntityModel(Competition)
  Competition: Repository<Competition>;
  @InjectEntityModel(Incubator)
  Incubator: Repository<Incubator>;
  @InjectEntityModel(Company)
  Company: Repository<Company>;
  @InjectEntityModel(Expert)
  Expert: Repository<Expert>;
  @InjectEntityModel(Unit)
  Unit: Repository<Unit>;
  @InjectEntityModel(School)
  School: Repository<School>;

  @Inject()
  ctx: Context;
  getQueryColumnsOpera(): object {
    return {};
  }

  // 用户认证
  async prove(query) {
    const { user } = query;
    const roleList = await this.model.find({ where: { is_use: Equal('0') } });
    const list = [];
    for (const val of roleList) {
      const tModel = this[val.code];
      if (tModel) {
        const info: any = val;
        if (val.code !== 'Admin' && val.code !== 'User' && val.code !== 'Competition') {
          const result = await tModel.findOne({ where: { user: Equal(user), status: Equal('1') } });
          if (result) info.title = `${val.name}(已认证)`;
          else info.title = `${val.name}(未认证)`;
          list.push(val);
        }
      }
    }
    return list;
  }

  // 判断用户角色是否审核成功
  async Isaudit(data) {
    const { id, role } = data;
    const list = [];
    for (const val of role) {
      const tModel = this[val];
      const roleInfo = await this.model.findOne({ where: { code: Equal(val), is_use: Equal('0') } });
      if (tModel) {
        if (val !== 'Admin' && val !== 'User') {
          const result = await tModel.findOne({ where: { user: Equal(id), status: Equal('1') } });
          if (!result) list.push(`${roleInfo.name}(未审核)`);
        }
      }
    }
    data.is_audit = list;
    return data;
  }

  //是否是超级管理员
  async isSuperAdmin() {
    const user = this.ctx.user;
    // if (isArray(user.role) && user.role.includes('Admin')) {
    if (user.is_super) {
      const qb = { id: user.id };
      const admin = await this.adminService.fetch(qb);
      if (!admin) return false;
      if (admin.is_super === '0') return true;
    } else return false;
  }
  /**
   * 获取用户接口权限编码列表
   * @returns {Array<string>} apiCodes 用户接口权限编码列表
   */
  async getUserApiCodes() {
    const user = this.ctx.user;
    const getControllerCode = list => {
      const result = [];
      for (const i of list) {
        const { config = [], children = [] } = i;
        const l = config.map(i => get(i, 'controller_code'));
        result.push(...l);
        if (children && children.length > 0) {
          const cl = getControllerCode(children);
          result.push(...cl);
        }
      }
      return result;
    };
    if (await this.isSuperAdmin()) {
      const smqb = { is_use: '0' };
      const menus = await this.menusService.queryMenu(smqb);
      // 需要整理出所有 config 中 methodCode = ${controllerCode}.${method} ,query 没有二级code
      let apiCodes = getControllerCode(menus);
      apiCodes = compact(apiCodes);
      return apiCodes;
    }
    // 其他用户需要把 部门 和 角色的权限叠加在一起
    // 部门权限
    const deptCodes = await this.getDeptCodes(get(user, 'dept'));
    // 角色权限
    const roleCodes = await this.getRoleCodes(get(user, 'role'));
    let allCodes = [...deptCodes, ...roleCodes];
    allCodes = uniq(allCodes);
    const qb = { route_name: allCodes };
    const operas = { route_name: this.Opera.In };
    const { data: menuList } = await this.menusService.query(qb, null, operas);
    const apiCodes = getControllerCode(menuList);
    return apiCodes;
  }
  /**
   * 获取用户菜单
   * @param needCode 是否只需要编码
   * @returns
   */
  async getUserMenus(needCode = false) {
    const user = this.ctx.user;
    let roles = get(user, 'role');
    if (!roles) roles = [];
    const userRoles = await this.model.createQueryBuilder().where(`"code" IN (:...val)`, { val: user.role }).getMany();
    if (!userRoles || userRoles.length <= 0) return;
    const hasAdmin = userRoles.filter(f => f.is_admin_role === '1');
    if (hasAdmin.length <= 0) {
      const mids = userRoles.map(i => i.menu);
      const menusCodes = uniq(flattenDeep(mids));
      const roleMenus = await this.getRoleUserMenus(menusCodes);
      return { menus: roleMenus, role_code: menusCodes };
    }
    // 平台的超级管理员有所有权限
    if (await this.isSuperAdmin()) {
      const menus = await this.menusService.queryMenu({ is_use: '0' });
      return { menus };
    }
    // 其他用户需要把 部门 和 角色的权限叠加在一起
    // 部门权限
    const deptCodes = await this.getDeptCodes(get(user, 'dept'));
    // 角色权限
    const roleCodes = await this.getRoleCodes(get(user, 'role'));
    let allCodes = [...deptCodes, ...roleCodes];
    allCodes = uniq(allCodes);
    // 只获取编码,直接返回
    if (needCode) return allCodes;
    // 根据编码获取目录数据
    const menuCodes = this.setMenuCodes(allCodes);
    const qb = { route_name: menuCodes };
    const operas = { route_name: this.Opera.In };
    const { data: menuList } = await this.menusService.query(qb, null, operas);
    let treeMenu = menuList.filter(f => !f.parent_id);
    treeMenu = this.menusService.treeMenu(menuList, treeMenu);
    return { menus: treeMenu, role_code: allCodes };
  }

  async getRoleUserMenus(roles) {
    const { data } = await this.userMenusService.query({ is_use: '0', route_name: roles }, { order: { order_num: 'ASC' } }, { route_name: this.Opera.In });
    return data;
  }

  /**
   * 整理出角色目录的编码,主要是与route_name编码对应
   * @param allCodes 本用户的角色+部门的完整权限编码
   * @returns 目录需要的编码
   */
  setMenuCodes(allCodes: Array<string>) {
    const result = [];
    for (const code of allCodes) {
      const arr = code.split('.');
      result.push(last(arr));
    }
    return uniq(result);
  }

  /**
   * 根据部门id获取权限列表
   * @param deptId 部门id
   * @returns 权限列表
   */
  async getDeptCodes(deptId: number) {
    if (deptId) {
      const qb = { id: deptId };
      const dept = await this.deptService.fetch(qb);
      if (!dept) return [];
      // 处理部门有的权限
      const resource = get(dept, 'resource', []);
      // const result = resource.map(i => {
      //   const arr = i.split('.');
      //   return last(arr);
      // });
      return resource;
    } else return [];
  }

  /**
   * 根据角色数组获取角色权限
   * @param roleCode 角色数据
   * @returns 权限列表
   */
  async getRoleCodes(roleCode: Array<string>) {
    if (!roleCode) return [];
    const roleCodes = [];
    for (const rc of roleCode) {
      roleCodes.push(lowerFirst(rc));
      roleCodes.push(upperFirst(rc));
    }
    // const role = await this.model.findOne({ where: { code: In(roleCode), is_use: '0' } });
    const role = await this.fetch({ code: roleCode }, { code: this.Opera.In });
    if (!role) return [];
    const roleMenu = get(role, 'menu', []);
    return roleMenu;
  }

  /**
   * 根据角色id数组,整理目录
   * @param roleIdList 角色id数组
   */
  async getMenuByRoles(roleIdList) {
    let menus = [];
    // 第一步,整理所有目录至一维数组
    for (const r of roleIdList) {
      menus.push(...flattenDeep(r));
    }
    menus = uniq(menus);
    const qb = { id: menus, is_use: '0' };
    const operas = { id: this.Opera.In };
    const { data: allMenu } = await this.menusService.query(qb, { order: { order_num: 'ASC' } }, operas);
    let treeMenu = allMenu.filter(f => !f.parent_id);
    treeMenu = this.menusService.treeMenu(allMenu, treeMenu);
    return treeMenu;
  }
}
