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 { ChannelService } from './channel';
import { MerchantService } from './merchant';
import { DispatchService } from './channels/dispatch';
import { Utils } from '../../../comm/utils';
import * as md5 from 'md5';
import { OrderService } from './order';
import { RateService } from './rate';
import { MchChannelService } from './mchChannel';

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

  @Inject()
  channelService: ChannelService;

  @Inject()
  mchChannelService: MchChannelService;

  @Inject()
  merchantService: MerchantService;

  @Inject()
  orderService: OrderService;

  @Inject()
  dispatchService: DispatchService;

  @Inject()
  rateService: RateService;

  @Logger()
  logger: ILogger;

  @Inject()
  utils: Utils;

  async query(payload) {
    try {
      this.logger.info('代收查询', JSON.stringify(payload));
      await this.validQueryParam(payload);
      const merchant = await this.validMerchant(payload.mchId);
      await this.validSign(payload, merchant);
      const order = await this.orderService.findByOutOrderNo(
        payload.outOrderNo
      );
      if (!order || order.mchId !== payload.mchId) {
        throw new Error('订单不存在');
      }
      return {
        mchId: payload.mchId,
        orderNo: order.orderNo,
        outOrderNo: payload.outOrderNo,
        amount: order.amount,
        status: order.status,
      };
    } 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 order(payload) {
    let order: any = null;
    try {
      this.logger.info('代收下单', JSON.stringify(payload));
      await this.validParam(payload);
      const merchant = await this.validMerchant(payload.mchId);
      const rate = await this.validRate(payload.mchId, payload.payType, payload.currency);
      const channel = await this.validPayWay(payload.mchId, payload.payType, payload.amount, payload.currency);
      this.dispatchService.validOrderParam(channel, payload);
      await this.validSign(payload, merchant);
      await this.validOutOrderNo(payload);
      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;
      }
      order = {
        orderNo: this.utils.createOrderNo('DS'),
        outOrderNo: payload.outOrderNo,
        traceNo: '',
        payType: payload.payType,
        code: channel.code,
        mchId: merchant.mchId,
        amount: payload.amount,
        channelCharge,
        charge,
        date: null,
        notifyUrl: payload.notifyUrl,
        returnUrl: payload.returnUrl,
        status: 0,
        notifyStatus: 0,
        remark: '',
        userId: payload.userId || '',
        userIp: payload.userIp || '',
        currency: payload.currency || 'USD'
      };
      await this.orderService.create(order);
      const { isDone = true, payUrl, traceNo } = await this.dispatchService.order(
        order,
        channel,
        payload
      );
      if (isDone) {
        order.payUrl = payUrl;
        order.traceNo = traceNo;
        await this.orderService.create(order);
      }
      return {
        orderNo: order.orderNo,
        outOrderNo: payload.outOrderNo,
        payUrl,
      };
    } catch (err) {
      this.logger.error('下单失败', JSON.stringify(payload), err.message);
      if (order) {
        order.remark = err.message;
        order.status = 2;
        await this.orderService.create(order);
      }
      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.userId)) {
      throw new Error('商户平台用户ID【userId】不能为空');
    }
    if (_.isEmpty(payload.currency)) {
      throw new Error('货币单位【currency】不能为空');
    }
    // if (!Number.isInteger(+payload.amount)) {
    //   throw new Error('支付金额【amount】必须为整数');
    // }
    if (_.isEmpty(payload.payType)) {
      throw new Error('支付方式【payType】不能为空');
    }
    if (_.isEmpty(payload.sign)) {
      throw new Error('签名【sign】不能为空');
    }
  }

  async validRate(mchId, payType, currency) {
    const rate = await this.rateService.getOne({
      mchId,
      payType,
      currency,
      type: 1
    });
    if (!rate) {
      throw new Error('商户该支付方式未开通');
    }
    return rate;
  }

  async validPayWay(mchId, payType, amount, currency) {
    const mchChannels = await this.mchChannelService.getByMchIdAndType(mchId, payType, currency);
    if (mchChannels.length === 0) {
      throw new Error('暂无可用支付路由');
    }
    let channels = await this.channelService.getByCodes(mchChannels.map(item => item.code));
    if (channels.length === 0) {
      throw new Error('暂无可用支付路由');
    }
    channels = channels.filter(item => item.currency === currency);
    // 筛选出金额范围符合的，并且计算出所有可用通道的最大金额和最小金额
    let allMax = 100000000;
    let allMin = 0;
    channels = channels.filter(item => {
      let max = item.max || 0;
      let min = item.min || 0;
      if (item.max < allMax) {
        allMax = item.max;
      }
      if (item.min > allMin) {
        allMin = item.min;
      }
      const mchChannel = mchChannels.find(i => i.code === item.code);
      item['weight'] = mchChannel.weight || 0;
      return +amount >= +min && +amount <= max
    })
    if (channels.length === 0) {
      throw new Error(`单笔最大限额【${+allMin}, ${+allMax}】`);
    }
    let weight = 0;
    channels.forEach(item => {
      weight += item['weight']
    });
    const ramdon = Math.floor(Math.random() * weight);
    let totalWeight = 0;
    for (let i = 0; i < channels.length; i++) {
      const item = channels[i];
      totalWeight += item['weight']
      if (ramdon < totalWeight) {
        return item;
      }
    }
  }

  async validMerchant(mchId) {
    const merchant = await this.merchantService.getByMchId(mchId);
    if (!merchant) {
      throw new Error('商户不存在');
    }
    if (+merchant.status !== 1) {
      throw new Error('商户状态异常');
    }
    return merchant;
  }

  async validSign(payload, merchant) {
    const data = { ...payload };
    const sign = data.sign;
    delete data.sign;
    const signStr = this.utils.signSort(data);
    const verifySign = md5(signStr + `&key=${merchant.key}`);
    this.logger.info(
      '加密结果',
      sign,
      signStr,
      verifySign,
      sign === verifySign
    );
    if (sign !== verifySign) {
      throw new Error(
        '验签失败，请确认签名是否正确，正确待签名字符串为：' + signStr
      );
    }
  }

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