import { Inject, Provide } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { In, Repository } from 'typeorm';
import { BaseService, QueryOpera } from '../../frame/BaseService';
import { Role } from '../../entity/system/role.entity';
import { Context } from '@midwayjs/koa';
import { AdminService } from './admin.service';
import { compact, flattenDeep, get, isArray, last, lowerFirst, uniq, upperFirst } from 'lodash';
import { DeptService } from './dept.service';
import { Opera } from '../../frame/dbOpera';
import { MenusService } from './menus.service';
@Provide()
export class RoleService extends BaseService<Role> {
  @InjectEntityModel(Role)
  model: Repository<Role>;

  @Inject()
  deptService: DeptService;

  @Inject()
  adminService: AdminService;

  @Inject()
  menusService: MenusService;
  @Inject()
  ctx: Context;
  //是否是超级管理员
  async isSuperAdmin() {
    const user = this.ctx.user;
    // if (isArray(user.role) && user.role.includes('Admin')) {
    if (user.is_super) {
      const qb: QueryOpera[] = [{ column: 'id', value: user.id, opera: [Opera.Equal] }];
      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 = [{ column: 'is_use', opera: [Opera.Equal], value: '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 = [{ column: 'route_name', opera: [Opera.In], value: allCodes }];
    const { data: menuList } = await this.menusService.query(qb);
    const apiCodes = getControllerCode(menuList);
    return apiCodes;
  }

  async getUserMenus(needCode = false) {
    const user = this.ctx.user;
    // 平台的超级管理员有所有权限
    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 = [{ column: 'route_name', opera: [Opera.In], value: menuCodes }];
    const { data: menuList } = await this.menusService.query(qb);
    let treeMenu = menuList.filter(f => !f.parent_id);
    treeMenu = this.menusService.treeMenu(menuList, treeMenu);
    return { menus: treeMenu, role_code: allCodes };
  }
  /**
   * 整理出角色目录的编码,主要是与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: QueryOpera[] = [{ column: 'id', value: deptId, opera: [Opera.Equal] }];
      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' } });
    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 = [
      { column: 'id', opera: [Opera.In], value: menus },
      { column: 'is_use', opera: [Opera.Equal], value: '0' },
    ];
    const { data: allMenu } = await this.menusService.query(qb, { order: { order_num: 'ASC' } });
    let treeMenu = allMenu.filter(f => !f.parent_id);
    treeMenu = this.menusService.treeMenu(allMenu, treeMenu);
    return treeMenu;
  }
}
