import { Inject, Logger, Provide } from '@midwayjs/decorator';
import { BaseService } from '@cool-midway/core';
import { HttpService } from '@midwayjs/axios';
import { ILogger } from '@midwayjs/logger';
import { CoolCommException } from '@cool-midway/core';
import * as _ from 'lodash';
import { WithdrawChannelService } from './withdrawChannel';
import { MerchantService } from './merchant';
import { DispatchService } from './channels/dispatch';
import { Utils } from '../../../comm/utils';
import * as md5 from 'md5';
import { WithdrawService } from './withdraw';
import { WalletService } from './wallet';
import { MchWithdrawChannelService } from './mchWithdrawChannel';
import { RateService } from './rate';
import { WithdrawStateService } from './withdrawState';
import { PayService } from './pay';

@Provide()
export class RepayService extends BaseService {
  @Inject()
  httpService: HttpService;

  @Inject()
  payService: PayService;

  @Inject()
  withdrawStateService: WithdrawStateService;

  @Inject()
  withdrawChannelService: WithdrawChannelService;

  @Inject()
  mchWithdrawChannelService: MchWithdrawChannelService;

  @Inject()
  merchantService: MerchantService;

  @Inject()
  withdrawService: WithdrawService;

  @Inject()
  dispatchService: DispatchService;

  @Inject()
  walletService: WalletService;

  @Inject()
  rateService: RateService;

  @Logger()
  logger: ILogger;

  @Inject()
  utils: Utils;

  async getBanks(payload) {
    this.logger.info('代付支持银行查询', JSON.stringify(payload));
    await this.validGetBanksParam(payload);
    const merchant = await this.payService.validMerchant(payload.mchId);
    await this.payService.validSign(payload, merchant);
    const channels = await this.withdrawChannelService.queryByCurrency(payload.currency);
    const result = await Promise.all(channels.map(item => {
      return this.dispatchService.getBanks(item, { currency: payload.currency });
    }))
    let list = [];
    result.forEach(item => {
      list = list.concat(item)
    })
    return list;
  }

  async validGetBanksParam(payload) {
    if (_.isEmpty(payload.mchId)) {
      throw new Error('商户号【mchId】不能为空');
    }
    if (_.isEmpty(payload.currency)) {
      throw new Error('货币单位【currency】不能为空');
    }
    if (_.isEmpty(payload.sign)) {
      throw new Error('签名【sign】不能为空');
    }
  }

  async queryBalance(payload) {
    this.logger.info('余额查询', JSON.stringify(payload));
    await this.validQueryBalanceParam(payload);
    const merchant = await this.payService.validMerchant(payload.mchId);
    await this.payService.validSign(payload, merchant);
    let wallet;
    if (payload.currency) {
      const data = await this.walletService.queryByMchIdAndCurrency(payload.mchId, payload.currency);
      if (data) {
        wallet = [data];
      } else {
        wallet = [];
      }
    } else {
      wallet = await this.walletService.queryByMchId(payload.mchId);
    }
    return wallet.map(item => {
      return {
        currency: item.currency,
        balance: item.balance,
        withdrawBalance: (+item.balance - Math.abs(+item.freeze)).toFixed(2),
        freeze: item.freeze,
      }
    })
  }

  async validQueryBalanceParam(payload) {
    if (_.isEmpty(payload.mchId)) {
      throw new Error('商户号【mchId】不能为空');
    }
    if (_.isEmpty(payload.sign)) {
      throw new Error('签名【sign】不能为空');
    }
  }

  async queryWithdraw(payload) {
    try {
      this.logger.info('代付查询', JSON.stringify(payload));
      await this.validQueryParam(payload);
      const merchant = await this.payService.validMerchant(payload.mchId);
      await this.payService.validSign(payload, merchant);
      const withdraw = await this.withdrawService.findByOutOrderNo(
        payload.outOrderNo
      );
      if (!withdraw || withdraw.mchId !== payload.mchId) {
        throw new Error('订单不存在');
      }
      return {
        mchId: payload.mchId,
        orderNo: withdraw.orderNo,
        outOrderNo: payload.outOrderNo,
        amount: withdraw.amount,
        status: withdraw.status,
        charge: withdraw.charge,
        message: withdraw.remark,
        currency: withdraw.currency
      };
    } catch (err) {
      this.logger.error('查询失败', JSON.stringify(payload), err.message);
      throw new CoolCommException(err.message);
    }
  }

  async validQueryParam(payload) {
    if (_.isEmpty(payload.mchId)) {
      throw new Error('商户号【mchId】不能为空');
    }
    if (_.isEmpty(payload.outOrderNo)) {
      throw new Error('商户订单号【outOrderNo】不能为空');
    }
    if (_.isEmpty(payload.sign)) {
      throw new Error('签名【sign】不能为空');
    }
  }

  async withdraw(payload) {
    let withdraw: any = null;
    try {
      this.logger.info('代付下单', JSON.stringify(payload));
      await this.validParam(payload);
      const merchant = await this.payService.validMerchant(payload.mchId);
      const rate = await this.validRate(payload.mchId, payload.currency);
      await this.payService.validSign(payload, merchant);
      await this.validOutOrderNo(payload.outOrderNo);
      const channel = await this.getChannel(merchant.mchId, payload.currency, payload.amount);
      let channelCharge = (+payload.amount * +channel.rate) / 100 + +channel.basicFee;
      if (channelCharge < channel.feeMin) {
        channelCharge = channel.feeMin;
      }
      let charge = (+payload.amount * +rate.rate) / 100 + +rate.basicFee;
      if (charge < rate.feeMin) {
        charge = rate.feeMin;
      }
      await this.validBalance(merchant, payload.currency, payload.amount + charge);
      withdraw = {
        orderNo: this.utils.createOrderNo('DS'),
        outOrderNo: payload.outOrderNo,
        traceNo: '',
        code: channel.code,
        mchId: merchant.mchId,
        amount: payload.amount,
        currency: payload.currency,
        personIdType: payload.personIdType,
        personId: payload.personId,
        personName: payload.personName,
        accountNo: payload.accountNo,
        bankCode: payload.bankCode,
        bankName: payload.bankName,
        channelCharge,
        charge,
        notifyUrl: payload.notifyUrl,
        status: this.withdrawStateService.STATUS.ACCEPT,
        remark: ''
      };
      await this.withdrawService.create(withdraw);
      await this.walletService.updateBalance({
        orderNo: withdraw.orderNo,
        mchId: withdraw.mchId,
        type: 2,
        amount: 0,
        currency: withdraw.currency,
        freeze: (+withdraw.amount + +withdraw.charge)
      })
      let status = withdraw.status;
      if (withdraw.code) {
        status = await this.withdrawStateService.stateTo(withdraw.status, this.withdrawStateService.STATUS.PROCESSIONG, withdraw);
      }
      return {
        mchId: payload.mchId,
        orderNo: withdraw.orderNo,
        outOrderNo: payload.outOrderNo,
        amount: withdraw.amount,
        status: status,
        charge: withdraw.charge,
        message: withdraw.remark,
        currency: withdraw.currency
      };
    } catch (err) {
      this.logger.error('代付失败', JSON.stringify(payload), err.message);
      throw new CoolCommException(err.message);
    }
  }

  async validParam(payload) {
    if (_.isEmpty(payload.mchId)) {
      throw new Error('商户号【mchId】不能为空');
    }
    if (_.isEmpty(payload.outOrderNo)) {
      throw new Error('商户订单号【outOrderNo】不能为空');
    }
    if (payload.outOrderNo.length < 8 || payload.outOrderNo.length > 32) {
      throw new Error('商户订单号的长度范围为：【8,32】');
    }
    if (_.isEmpty(payload.notifyUrl)) {
      throw new Error('代付成功回调地址【notifyUrl】不能为空');
    }
    if (_.isEmpty(payload.amount + '')) {
      throw new Error('金额【amount】不能为空');
    }
    if (+payload.amount <= 0) {
      throw new Error('金额【amount】必须大于0');
    }
    if (_.isEmpty(payload.currency)) {
      throw new Error('货币单位【currency】不能为空');
    }
    if (_.isEmpty(payload.personIdType)) {
      throw new Error('用户证件信息【personIdType】不能为空');
    }
    if (_.isEmpty(payload.personId)) {
      throw new Error('用户证件号【personId】不能为空');
    }
    if (_.isEmpty(payload.personName)) {
      throw new Error('用户姓名【personName】不能为空');
    }
    if (_.isEmpty(payload.accountNo)) {
      throw new Error('银行账号【accountNo】不能为空');
    }
    if (_.isEmpty(payload.bankName)) {
      throw new Error('银行名称【bankName】不能为空');
    }
    if (_.isEmpty(payload.bankCode)) {
      throw new Error('银行编码【bankCode】不能为空');
    }
    if (_.isEmpty(payload.sign)) {
      throw new Error('签名【sign】不能为空');
    }
  }

  async getChannel(mchId, currency, amount) {
    const mchWithdrawChannel = await this.mchWithdrawChannelService.getByMchIdAndCurrency(mchId, currency);
    if (!mchWithdrawChannel) {
      return {
        code: '',
        rate: 0,
        basicFee: 0,
        feeMin: 0
      };
    }
    const channel = await this.withdrawChannelService.queryByCode(mchWithdrawChannel.code);
    if (!channel) {
      return {
        code: '',
        rate: 0,
        basicFee: 0,
        feeMin: 0
      };
    }
    let max = channel.max || 0;
    let min = channel.min || 0;
    if (+amount > +max || +amount < +min) {
      throw new Error(`单笔最大限额【${+min}, ${+max}】`);
    }
    return channel;
  }

  async validRate(mchId, currency) {
    const rate = await this.rateService.getOne({
      mchId,
      currency,
      type: 2
    });
    if (!rate) {
      throw new Error('商户未开通代付渠道');
    }
    return rate;
  }

  async validOutOrderNo(outOrderNo) {
    const order = await this.withdrawService.findByOutOrderNo(outOrderNo);
    if (order) {
      throw new Error('商户订单号已存在，请勿重复下单');
    }
  }

  async validBalance(merchant, currency, amount) {
    const wallet = await this.walletService.queryByMchIdAndCurrency(merchant.mchId, currency);
    const { balance = 0, freeze = 0 } = wallet;
    const withdrawAmount = (+balance - Math.abs(+freeze)).toFixed(2)
    if (+amount > +withdrawAmount) {
      throw new Error('当前商户余额不足，请确认！当前商户可提现余额:' + +withdrawAmount);
    }
  }
}
