import { Inject, MidwayWebRouterService, Provide } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import * as toqm from 'typeorm';
import { get, pick } from 'lodash';
import dayjs = require('dayjs');
import { Context } from '@midwayjs/koa';
import { Opera as operaModel } from '../../entityLogs/opera.entity';
import { QueryOpera } from '../../frame/BaseService';
import { Opera } from '../../frame/dbOpera';

@Provide()
export class OperaService  {
  @Inject()
  ctx: Context;
  @InjectEntityModel(operaModel, 'logs')
  model: Repository<operaModel>;
  @Inject()
  webRouterService: MidwayWebRouterService;

  /**
   * 生成日志
   * 原数据和新数据格式:
   * {
   *    [表名]:[数据,...数据]
   * }
   * @param origin_data 原数据
   * @param new_data 新数据
   */
  async toMakeLogs(origin_data?: object, new_data?: object) {
    const user = this.ctx.user;
    if (user && user.id) {
      const req = this.ctx.request;
      const logInfo: any = {
        // 操作人
        operator_id: user.id,
        operator_name: get(user, 'nick_name', get(user, 'name')),
        // ip
        ip: get(req, 'header.x-forwarded-for', req.ip),
        // 设备
        device: get(req, 'header.user-agent'),
        // 操作时间
        time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
        // 操作地址
        referer: get(req, 'header.referer'),
        // 请求地址uri
        path: get(req, 'path'),
        // 地址参数
        params: get(req, 'params'),
        // 请求参数
        query: get(req, 'query'),
        // 方法体参数
        body: get(req, 'body'),
        // 操作业务:可以通过路由注解的routerName来获取
        opera: null,
        // 变动值: 表:[数据] 需要自己组织
        origin_data: origin_data,
        new_data: new_data,
      };
      const routeInfo = await this.webRouterService.getMatchedRouterInfo(this.ctx.path, this.ctx.method);
      if (routeInfo) logInfo.opera = get(routeInfo, 'routerName');
      await this.model.insert(logInfo);
    }
  }


  /**
   * 列表查询
   * @param query 查询条件(经过queryBuilder转换后的)
   * @param param 有关skip和limit,order_by(数组,多字段排列)
   */
  async query(query: QueryOpera[], { skip = 0, limit = 0, order = {} } = {}) {
    let findOpera: any = {};
    // 组织查询顺序
    let orderObject: any = {};
    // 如果有自定义顺序,则按照自定义顺序来, 没有自定义顺序,默认按创建时间的desc查询
    if (Object.keys(order).length > 0) {
      for (const column in order) orderObject[column] = order[column];
    } else orderObject = { time: 'DESC' };
    if (query.length > 0) findOpera = this.buildFindWhere(query);
    const builder = await this.model.createQueryBuilder().setFindOptions({ where: findOpera, skip, take: limit, order: orderObject });
    const data = await builder.getMany();
    const total = await builder.getCount();
    return { data, total };
  }

  /**根据builder设置转换成options */
  buildFindWhere(query) {
    const result = {};
    for (const qo of query) {
      let val = qo.value;
      for (const opera of qo.opera) {
        const methods = pick(toqm, ['IsNull', 'In', 'MoreThanOrEqual', 'MoreThan', 'LessThanOrEqual', 'LessThan', 'Equal', 'Not', 'Like', 'ILike', 'Between']);
        // const m = get(methods, Opera[opera]);
        const m = methods[Opera[opera]];
        // if 和 else if 都是需要特殊处理值的情况
        // Between这个一般都是最先处理,要放到数组最前面,否则会影响value的值
        if (Opera[opera] === Opera.Between) val = m(get(val, 0), get(val, 1));
        else if (Opera[opera] === Opera.Like || Opera[opera] === Opera.ILike) val = m(`%${val}%`);
        else if (Opera[opera] === Opera.LikeLeft || Opera[opera] === Opera.ILikeLeft) val = m(`%${val}`);
        else if (Opera[opera] === Opera.LikeRight || Opera[opera] === Opera.ILikeRight) val = m(`${val}%`);
        else if (Opera[opera] === Opera.IsNull) val = m![Opera[opera]]();
        else val = m(val);
      }
      result[qo.column] = val;
    }
    return result;
  }


  /**
   * 默认查询条件object
   * integer: 数字 默认 全等
   * character varying: 字符串 默认 模糊
   * timestamp without time zone: 时间 默认不处理,手写处理方式
   * @param {object} query 查询条件object
   * @return 输出为查询数组
   * [
   *  {
   *    column:字段,
   *    opera:查询方式
   *    value:具体值
   *  }
   * ]
   *  e.g.:
   * [
   *  {
   *    column:'nick_name',
   *    opera:eq,
   *    value:'admin'
   *  }
   * ]
   * 形成查询数组,再由query函数处理具体查询
   */
  queryBuilder(query: any = {}) {
    /**列设置,所有字段都需要设置type */
    const columns = this.model.metadata.columns;
    /**将array的列设置 转换为object,以便query使用*/
    const columnsObject = {};
    // 整理成object
    for (const c of columns) columnsObject[c.propertyName] = c.type.toString();
    /**返回的查询操作数组 */
    const queryArray: QueryOpera[] = [];
    // 根据查询条件(query),对字段进行处理
    for (const column in query) {
      const dataType = columnsObject[column];
      // 没找到字段,那就直接跳过,按理说不应该找不到.但是真的找不到,也不能添加进查询条件里,会出错
      if (!dataType) continue;
      if (dataType === 'integer') {
        const qo: QueryOpera = { column, value: query[column], opera: [Opera.Equal] };
        queryArray.push(qo);
      } else if (dataType === 'character varying') {
        const qo: QueryOpera = { column, value: query[column], opera: [Opera.Like] };
        queryArray.push(qo);
      } else if (dataType === 'timestamp without time zone') {
        //默认不处理
      }
    }

    return queryArray;
  }


  /**单查询，不止用id还可以根据别的条件 */
  async fetch(query: QueryOpera[]) {
    const whereOptions = this.buildFindWhere(query);
    const builder = this.model.createQueryBuilder().setFindOptions({ where: whereOptions });
    const result = await builder.getOne();
    return result;
  }


}
