import { Inject, Logger, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm';
import * as _ from 'lodash';
import { CoolCommException } from '@cool-midway/core';
import { MerchantEntity } from '../entity/merchant';
import { Utils } from '../../../comm/utils';
import { DispatchService } from './channels/dispatch';
import { WalletService } from './wallet';
import { RefundEntity } from '../entity/refund';
import { ILogger } from '@midwayjs/logger';

@Provide()
export class RefundService extends BaseService {
  @InjectEntityModel(RefundEntity)
  refundEntity: Repository<RefundEntity>;

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

  @Inject()
  walletService: WalletService;

  @Inject()
  dispatchService: DispatchService;

  @Inject()
  ctx;

  @Inject()
  utils: Utils;

  @Logger()
  logger: ILogger;

  async findByRefundNo(refundNo: any) {
    return await this.refundEntity.findOneBy({ refundNo });
  }

  async toRefund(order, amount, reson) {
    const refund: any = {
      orderNo: order.orderNo,
      outOrderNo: order.outOrderNo,
      traceNo: order.traceNo,
      refundNo: this.utils.createOrderNo('TK'),
      refundTraceNo: '',
      code: order.code,
      mchId: order.mchId,
      amount,
      charge: order.charge,
      currency: order.currency,
      reson,
      date: null,
      status: 0,
      remark: '',
      createTime: new Date(),
      updateTime: new Date()
    }
    const wallet = await this.walletService.queryByMchIdAndCurrency(refund.mchId, refund.currency);
    const { balance = 0, freeze = 0 } = wallet;
    const withdrawAmount = +balance - Math.abs(+freeze)
    if (+refund.amount > +withdrawAmount) {
      throw new Error('当前商户余额不足，无法发起退款！当前商户可提现余额:' + withdrawAmount);
    }
    await this.refundEntity.insert(refund);
    await this.walletService.updateBalance({
      orderNo: refund.orderNo,
      mchId: refund.mchId,
      type: 5,
      amount: 0,
      currency: refund.currency,
      freeze: +refund.amount
    });
    try {
      this.logger.error('发起退款', refund.orderNo, refund.refundNo);
      const { refundTraceNo } = await this.dispatchService.refund(refund);
      refund.refundTraceNo = refundTraceNo;
      await this.refundEntity.update(refund.id, refund);
      this.startQueryRefund(refund.id);
      return {
        mchId: refund.mchId,
        orderNo: refund.orderNo,
        outOrderNo: refund.outOrderNo,
        refundNo: refund.refundNo,
        amount: refund.amount,
        currency: refund.currency,
        charge: refund.charge,
        status: refund.status,
        reson: refund.reson,
        remark: refund.remark
      }
    } catch (err) {
      this.logger.error('发起退款失败', refund.orderNo, err.message);
      await this.updateRefundFail(refund, err.message);
      throw new CoolCommException(err.message);
    }
  }

  async startQueryRefund(id, times = 10) {
    await new Promise(resolve => {
      setTimeout(() => { resolve(1) }, 8000);
    })
    if (times === 0) {
      return;
    }
    try {
      const result = await this.queryByApi(id);
      if (+result.status === 0) {
        this.startQueryRefund(id, times - 1);
      }
    } catch (e) {
      this.startQueryRefund(id, times - 1);
    }
  }

  async updateRefundFail(refund, message) {
    refund.remark = message;
    refund.status = 2;
    refund.updateTime = new Date();
    await this.refundEntity.update(refund.id, refund);
    await this.walletService.updateBalance({
      orderNo: refund.orderNo,
      mchId: refund.mchId,
      type: 5,
      amount: 0,
      currency: refund.currency,
      freeze: -refund.amount
    });
    return true;
  }

  async updateRefundSuccess(data) {
    const refund = await this.refundEntity.findOneBy({ id: data.id });
    if (!refund || +refund.status === 1 || +refund.status === 2) {
      // 订单早就已退款成功，无需做任何操作
      return;
    }
    refund.status = 1;
    refund.updateTime = new Date();
    refund.date = data.date || new Date();
    refund.refundTraceNo = data.refundTraceNo;
    await this.refundEntity.update(refund.id, refund);
    await this.walletService.updateBalance({
      orderNo: refund.orderNo,
      mchId: refund.mchId,
      type: 5,
      amount: -refund.amount,
      currency: refund.currency,
      freeze: -refund.amount
    });
    return true;
  }

  async queryByApi(id) {
    const refund = await this.refundEntity.findOneBy({ id });
    if (!refund) {
      throw new CoolCommException('订单不存在');
    }
    if (+refund.status === 0) {
      const { status, message, refundTraceNo, date } =
        await this.dispatchService.queryRefund(refund);
      if (+status === 1) {
        // 更新订单和更新余额
        refund.refundTraceNo = refundTraceNo || refund.refundTraceNo;
        refund.date = date;
        await this.updateRefundSuccess(refund);
      } else if (+status === 2) {
        refund.refundTraceNo = refundTraceNo || refund.refundTraceNo;
        refund.date = date;
        await this.updateRefundFail(refund, message);
      }
      return { status, message };
    } else {
      return { status: refund.status, message: "订单已有结果，请勿重复查询" }
    }
  }

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

  async page(query) {
    let {
      orderNo = '',
      outOrderNo = '',
      traceNo = '',
      refundNo = '',
      refundTraceNo = '',
      mchId = '',
      status = '',
      createTime = [],
    } = query;
    if (!createTime || createTime.length !== 2) {
      throw new CoolCommException('请选择日期范围');
    }
    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 * FROM dj_refund 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.setSql(refundNo, 'and a.refundNo like ?', [`%${refundNo}%`])}
      ${this.setSql(refundTraceNo, 'and a.refundTraceNo like ?', [`%${refundTraceNo}%`])}
      ${this.utils.isMerchant(roleIds)
        ? this.setSql(mchId, 'and a.mchId = ?', [`${mchId}`])
        : this.setSql(mchId, 'and a.mchId like ?', [`%${mchId}%`])
      }
      ${this.setSql(status, 'and a.status = ?', [status])}
      ${this.setSql(
        createTime && createTime.length === 2,
        'and (a.createTime between ? and ?)',
        createTime
      )}
    `;
    return this.sqlRenderPage(sql, query);
  }
}
