import { Inject, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import * as _ from 'lodash';
import { OrderEntity } from '../entity/order';
import { CoolCommException } from '@cool-midway/core';
import { NotifyService } from './notify';
import * as moment from 'moment';
import { MerchantEntity } from '../entity/merchant';
import { Utils } from '../../../comm/utils';
import { DispatchService } from './channels/dispatch';
import { ChannelService } from './channel';
import { WalletService } from './wallet';
import { RefundService } from './refund';

@Provide()
export class OrderService extends BaseService {
  @InjectEntityModel(OrderEntity)
  orderEntity: Repository<OrderEntity>;

  @InjectEntityModel(MerchantEntity)
  merchantEntity: Repository<MerchantEntity>;

  @Inject()
  walletService: WalletService;

  @Inject()
  notifyService: NotifyService;

  @Inject()
  dispatchService: DispatchService;

  @Inject()
  channelService: ChannelService;

  @Inject()
  refundService: RefundService;

  @Inject()
  ctx;

  @Inject()
  utils: Utils;

  async toRefund(payload) {
    const order = await this.orderEntity.findOneBy({ orderNo: payload.orderNo });
    if (!order) {
      throw new CoolCommException('订单不存在');
    }
    if (+order.status !== 1) {
      throw new CoolCommException('订单未支付成功');
    }
    return await this.refundService.toRefund(order, payload.amount, payload.reson);
  }

  async handleNotify(code, payload, headers) {
    const data = await this.dispatchService.handleOrderNotify(code, payload, headers);
    const order = await this.orderEntity.findOneBy({ orderNo: data.orderNo });
    order.date = data.date;
    order.traceNo = data.traceNo;
    if (+data.status === 1) {
      await this.updateOrderSuccess(order, true);
    } else if (+data.status === 3) {
      await this.updateOrderFail(order, true);
    }
  }

  async update(param) {
    const { roleIds } = this.ctx.admin;
    if (this.utils.isMerchant(roleIds)) {
      return;
    }
    const order = await this.orderEntity.findOneBy({ id: param.id });
    if (!order) {
      throw new Error('订单不存在');
    }
    await this.orderEntity.update(order.id, {
      traceNo: param.traceNo,
      date: param.date,
      remark: param.remark,
      updateTime: new Date(),
    });
    if (+order.status !== 1 && +param.status === 1) {
      await this.updateOrderSuccess(order, false);
    }
  }

  async notify(id) {
    const order = await this.orderEntity.findOneBy({ id });
    if (order && (+order.status === 1 || +order.status === 3)) {
      return await this.notifyService.notify(order, true);
    }
  }

  async queryByApi(id) {
    const order = await this.orderEntity.findOneBy({ id });
    if (!order) {
      throw new CoolCommException('订单不存在');
    }
    if (+order.status === 0 || +order.status === 2) {
      const { status, message, traceNo, date } =
        await this.dispatchService.query(order);
      if (+status === 1) {
        // 更新订单和更新余额
        order.traceNo = traceNo;
        order.date = date;
        await this.updateOrderSuccess(order);
      } else if (+status === 3) {
        // 更新订单和更新余额
        order.traceNo = traceNo;
        order.date = date;
        await this.updateOrderFail(order);
      }
      return { status, message };
    } else {
      return order.status;
    }
  }

  async updateOrderSuccess(data, notify = true) {
    const order = await this.orderEntity.findOneBy({ id: data.id });
    if (!order || +order.status === 1) {
      // 订单早就已成功，无需做任何操作
      return;
    }
    order.status = 1;
    order.updateTime = new Date();
    order.date = data.date || new Date();
    order.traceNo = data.traceNo;
    await this.orderEntity.update(order.id, order);
    await this.walletService.updateBalance({
      orderNo: order.orderNo,
      mchId: order.mchId,
      type: 1,
      amount: +order.amount - +order.charge,
      currency: order.currency,
      freeze: 0
    });
    if (notify) {
      // 发起通知
      this.notifyService.notify(order);
    }
    return true;
  }

  async updateOrderFail(data, notify = true) {
    const order = await this.orderEntity.findOneBy({ id: data.id });
    if (!order || +order.status === 3) {
      // 订单早就已取消，无需做任何操作
      return;
    }
    order.status = 3;
    order.updateTime = new Date();
    order.date = data.date || new Date();
    order.traceNo = data.traceNo;
    await this.orderEntity.update(order.id, order);
    if (notify) {
      // 发起通知
      this.notifyService.notify(order);
    }
    return true;
  }

  async create(order: any) {
    if (order.id) {
      order.updateTime = new Date();
      await this.orderEntity.update(order.id, order);
    } else {
      order.createTime = new Date();
      order.updateTime = new Date();
      await this.orderEntity.insert(order);
    }
    return order.id;
  }

  async findByOutOrderNo(outOrderNo: any) {
    return await this.orderEntity.findOneBy({ outOrderNo });
  }

  async findByOrderNo(orderNo: any) {
    return await this.orderEntity.findOneBy({ orderNo: orderNo });
  }

  async summary(query) {
    let {
      orderNo = '',
      outOrderNo = '',
      traceNo = '',
      mchId = '',
      payType = '',
      code = '',
      status = '',
      notifyStatus = '',
      createTime = [],
    } = query;
    if (!createTime || createTime.length !== 2) {
      return {
        num1: 0,
        num2: 0,
        num3: 0,
        num4: 0,
        num5: 0,
      };
    }
    if (moment(createTime[1]).diff(moment(createTime[0]), 'days') > 30) {
      return {
        num1: 0,
        num2: 0,
        num3: 0,
        num4: 0,
        num5: 0,
      };
    }
    const { roleIds, userId } = this.ctx.admin;
    if (this.utils.isMerchant(roleIds)) {
      const merchant = await this.merchantEntity.findOneBy({ userId });
      if (merchant) {
        mchId = merchant.mchId;
      } else {
        mchId = '-1';
      }
    }
    const sql = `SELECT 
    SUM(IF(a.status = 1, a.amount, 0)) as num1,
    SUM(IF(a.status = 1, a.charge, 0)) as num2,
    SUM(IF(a.status = 1, 1, 0)) as num3,
    count(*) as num4
    ${this.utils.isMerchant(roleIds)
        ? ''
        : ',SUM(IF(a.status = 1, a.channelCharge, 0)) as num5'
      }
    FROM dj_order a WHERE 1=1
      ${this.setSql(orderNo, 'and a.orderNo like ?', [`%${orderNo}%`])}
      ${this.setSql(outOrderNo, 'and a.outOrderNo like ?', [`%${outOrderNo}%`])}
      ${this.setSql(traceNo, 'and a.traceNo like ?', [`%${traceNo}%`])}
      ${this.utils.isMerchant(roleIds)
        ? this.setSql(mchId, 'and a.mchId = ?', [`${mchId}`])
        : this.setSql(mchId, 'and a.mchId like ?', [`%${mchId}%`])
      }
      ${this.setSql(payType, 'and a.payType = ?', [payType])}
      ${this.setSql(code, 'and a.code = ?', [code])}
      ${this.setSql(status, 'and a.status = ?', [status])}
      ${this.setSql(notifyStatus, 'and a.notifyStatus = ?', [notifyStatus])}
      ${this.setSql(
        createTime && createTime.length === 2,
        'and (a.createTime between ? and ?)',
        createTime
      )}
    `;
    return this.nativeQuery(sql);
  }

  async page(query) {
    let {
      orderNo = '',
      outOrderNo = '',
      traceNo = '',
      mchId = '',
      payType = '',
      code = '',
      status = '',
      notifyStatus = '',
      createTime = [],
    } = query;
    if (!createTime || createTime.length !== 2) {
      throw new CoolCommException('请选择日期范围');
    }
    if (moment(createTime[1]).diff(moment(createTime[0]), 'days') > 30) {
      throw new CoolCommException('日期范围最大只能30天');
    }
    const { roleIds, userId } = this.ctx.admin;
    if (this.utils.isMerchant(roleIds)) {
      const merchant = await this.merchantEntity.findOneBy({ userId });
      if (merchant) {
        mchId = merchant.mchId;
      } else {
        mchId = '-1';
      }
    }
    const sql = `SELECT a.id, a.orderNo, a.outOrderNo, a.payUrl, a.traceNo, a.mchId,  a.code, a.payType, a.amount,${this.utils.isMerchant(roleIds) ? '' : 'a.channelCharge,'
      } a.charge, a.status, a.notifyStatus, a.userId, a.userIp, a.currency, a.date, a.notifyUrl, a.returnUrl, a.remark, a.createTime, a.updateTime FROM dj_order a WHERE 1=1
      ${this.setSql(orderNo, 'and a.orderNo like ?', [`%${orderNo}%`])}
      ${this.setSql(outOrderNo, 'and a.outOrderNo like ?', [`%${outOrderNo}%`])}
      ${this.setSql(traceNo, 'and a.traceNo like ?', [`%${traceNo}%`])}
      ${this.utils.isMerchant(roleIds)
        ? this.setSql(mchId, 'and a.mchId = ?', [`${mchId}`])
        : this.setSql(mchId, 'and a.mchId like ?', [`%${mchId}%`])
      }
      ${this.setSql(payType, 'and a.payType = ?', [payType])}
      ${this.setSql(code, 'and a.code = ?', [code])}
      ${this.setSql(status, 'and a.status = ?', [status])}
      ${this.setSql(notifyStatus, 'and a.notifyStatus = ?', [notifyStatus])}
      ${this.setSql(
        createTime && createTime.length === 2,
        'and (a.createTime between ? and ?)',
        createTime
      )}
    `;
    return this.sqlRenderPage(sql, query);
  }

  async getDailyAmount(mchId, userId, code) {
    const time = [
      moment().startOf('day').format('YYYY-MM-DD') + ' 00:00:00',
      moment().endOf('day').format('YYYY-MM-DD') + ' 23:59:59',
    ];
    const sql = `SELECT SUM(IF(a.status = 1, a.amount, 0)) as total FROM dj_order a WHERE 1=1 
    ${this.setSql(mchId, 'and a.mchId = ?', [`${mchId}`])}
    ${this.setSql(userId, 'and a.userId = ?', [`${userId}`])}
    ${this.setSql(code, 'and a.code = ?', [`${code}`])}
    ${this.setSql(true, 'and (a.createTime between ? and ?)', time)}
    `
    return await this.nativeQuery(sql);
  }

  async getWeeklyAmount(mchId, userId, code) {
    const time = [
      moment().startOf('week').format('YYYY-MM-DD') + ' 00:00:00',
      moment().endOf('week').format('YYYY-MM-DD') + ' 23:59:59',
    ];
    const sql = `SELECT SUM(IF(a.status = 1, a.amount, 0)) as total FROM dj_order a WHERE 1=1 
    ${this.setSql(mchId, 'and a.mchId = ?', [`${mchId}`])}
    ${this.setSql(userId, 'and a.userId = ?', [`${userId}`])}
    ${this.setSql(code, 'and a.code = ?', [`${code}`])}
    ${this.setSql(true, 'and (a.createTime between ? and ?)', time)}
    `
    return await this.nativeQuery(sql);
  }

  async getMonthlyAmount(mchId, userId, code) {
    const time = [
      moment().startOf('month').format('YYYY-MM-DD') + ' 00:00:00',
      moment().endOf('month').format('YYYY-MM-DD') + ' 23:59:59',
    ];
    const sql = `SELECT SUM(IF(a.status = 1, a.amount, 0)) as total FROM dj_order a WHERE 1=1 
    ${this.setSql(mchId, 'and a.mchId = ?', [`${mchId}`])}
    ${this.setSql(userId, 'and a.userId = ?', [`${userId}`])}
    ${this.setSql(code, 'and a.code = ?', [`${code}`])}
    ${this.setSql(true, 'and (a.createTime between ? and ?)', time)}
    `
    return await this.nativeQuery(sql);
  }
}
