import { Init, Inject, Provide } from '@midwayjs/core';
import { BaseService, CoolCommException, CoolTransaction } from '@cool-midway/core';
import { PluginService } from '../../plugin/service/info';
import { OrderInfoService } from './info';
import { UserWxService } from '../../user/service/wx';
import BigNumber from 'bignumber.js';
import { OrderInfoEntity } from '../entity/info';
import { InjectEntityModel } from '@midwayjs/typeorm';
import { QueryRunner, Repository } from 'typeorm';
import { Action, OrderQueue } from '../queue/order';
import { BaseSysParamService } from '../../base/service/sys/param';
import { UserInfoEntity } from '../../user/entity/info';
import { RecycleTransactionEntity } from '../../shop/entity/transaction';

/**
 * 支付
 */
@Provide()
export class OrderPayService extends BaseService {
  @InjectEntityModel(OrderInfoEntity)
  orderInfoEntity: Repository<OrderInfoEntity>;

  @InjectEntityModel(UserInfoEntity)
  userInfoEntity: Repository<UserInfoEntity>;

  @InjectEntityModel(RecycleTransactionEntity)
  recycleTransactionEntity: Repository<RecycleTransactionEntity>;

  @Inject()
  pluginService: PluginService;

  @Inject()
  orderInfoService: OrderInfoService;

  @Inject()
  userWxService: UserWxService;

  @Inject()
  orderQueue: OrderQueue;

  @Inject()
  baseSysParamService: BaseSysParamService;

  @Init()
  async init() {
    await super.init();
  }

  /**
   * 支付成功
   * @param orderNum 订单号
   * @param payType 支付方式 0-待支付 1-微信 2-支付宝 3-碳积分
   */
  async paySuccess(orderNum: string, payType: number) {
    const order = await this.orderInfoService.getByOrderNum(orderNum);
    if (order && order.status == 0) {
      await this.orderInfoEntity.update(order.id, {
        payType,
        status: 1,
        payTime: new Date(),
      });
      // 发送自动确认收货队列
      const orderConfirm = await this.baseSysParamService.dataByKey(
        'orderConfirm'
      );
      this.orderQueue.add(
        { orderId: order.id, action: Action.CONFIRM },
        {
          // 自动确认收货时间
          delay: orderConfirm * 60 * 60 * 1000,
        }
      );
    }
    return 'success';
  }

  /**
   * 微信小程序支付
   * @param userId
   * @param orderId
   */
  async wxMiniPay(orderId: number, userId: string) {
    await this.orderInfoEntity.update(orderId, { wxType: 0 });
    return await this.wxJSAPI(orderId, userId, 0);
  }

  /**
   * 微信公众号支付
   * @param userId
   * @param orderId
   */
  async wxMpPay(orderId: number, userId: string) {
    await this.orderInfoEntity.update(orderId, { wxType: 1 });
    return await this.wxJSAPI(orderId, userId, 1);
  }

  /**
   * 获得appid
   * @param type 0-小程序 1-公众号 2-App
   */
  async getAppidByType(type: number) {
    let account;
    const plugin: any = await this.pluginService.getInstance('wx');
    // 小程序
    if (type == 0) {
      account = (await plugin.MiniApp()).getAccount();
    }
    // 公众号
    if (type == 1) {
      account = (await plugin.OfficialAccount()).getAccount();
    }
    // App
    if (type == 2) {
      account = (await plugin.OpenPlatform()).getAccount();
    }
    // 获得appid
    const appid = account.getAppId();
    return appid;
  }

  /**
   * 获得微信支付 SDK 实例
   * @param type 0-小程序 1-公众号 2-App
   * @returns
   */
  async wxPayInstance(appid: string) {
    // 获得插件实例
    const plugin: any = await this.pluginService.getInstance('pay-wx');
    // 获得插件配置
    const config = await plugin.getConfig();
    // 获得微信支付 SDK 实例
    const instance = await plugin.getInstance({
      ...config,
      appid,
    });
    return { config, instance };
  }

  /**
   * 微信APP支付
   * @param orderId
   * @param userId
   * @returns
   */
  async wxAppPay(orderId: number, userId: string) {
    const appid = await this.getAppidByType(2);

    const { config, instance } = await this.wxPayInstance(appid);

    const order = await this.getOrder(orderId, userId);
    const params = {
      description: '商品采购',
      out_trade_no: order?.orderNum,
      notify_url: config.notify_url,
      amount: {
        total: new BigNumber(order.price)
          .minus(order.discountPrice || 0)
          .multipliedBy(100)
          .toNumber(),
      },
    };

    const result = await instance.transactions_app(params);
    return result;
  }

  /**
   * 获得订单
   * @param orderId
   * @param userId
   * @returns
   */
  async getOrder(orderId: number, userId: string) {
    const order: OrderInfoEntity = await this.orderInfoService.info(orderId);
    if (!order || order.status != 0 || order.userId != userId) {
      throw new CoolCommException('订单不存在或不是可以支付的状态');
    }
    return order;
  }

  /**
   * 微信JSAPI
   * @param orderId
   * @param userId
   * @param type 0-小程序 1-公众号 2-App
   * @returns
   */
  async wxJSAPI(orderId: number, userId: string, type = 0) {
    const order = await this.getOrder(orderId, userId);
    const openid = await this.userWxService.getOpenid(userId, type);

    const appid = await this.getAppidByType(type);

    const { config, instance } = await this.wxPayInstance(appid);

    const params = {
      description: '商品采购',
      out_trade_no: order.orderNum,
      notify_url: config.notify_url,
      amount: {
        total: new BigNumber(order.price)
          .minus(order.discountPrice || 0)
          .multipliedBy(100)
          .toNumber(),
      },
      payer: {
        openid,
      },
      scene_info: {
        payer_client_ip: '127.0.0.1',
      },
    };
    const result = await instance.transactions_jsapi(params);
    return result;
  }

  /**
   * 微信退款
   * @param order
   * @param amount
   * @returns
   */
  async wxRefund(order: OrderInfoEntity, amount: number) {
    const appid = await this.getAppidByType(order.wxType);
    const { config, instance } = await this.wxPayInstance(appid);
    const params = {
      out_trade_no: order.orderNum,
      out_refund_no: order.refund.orderNum,
      notify_url: config.notify_url,
      amount: {
        refund: new BigNumber(amount).multipliedBy(100).toNumber(),
        total: new BigNumber(order.price).multipliedBy(100).toNumber(),
        currency: 'CNY',
      },
    };
    const result = await instance.refunds(params);
    if (
      result.status == 200 ||
      result.status == 'SUCCESS' ||
      result.status == 'PROCESSING'
    ) {
      return true;
    }
    throw new CoolCommException(result.message);
  }

  /**
   * 读取碳积分兑换比例，默认 100
   */
  async getCarbonRatio() {
    try {
      const value = await this.baseSysParamService.dataByKey('carbonratio');
      const ratio = Number(value);
      if (ratio > 0) {
        return ratio;
      }
    } catch (e) {}
    return 100;
  }

  /**
   * 碳积分支付：不调用外部渠道
   * 扣减积分 = 订单实付金额 * carbonratio
   */
  @CoolTransaction()
  async carbonPay(orderId: number, userId: string, queryRunner?: QueryRunner) {
    if (!queryRunner) {
      throw new CoolCommException('事务启动失败');
    }

    const manager = queryRunner.manager;
    const order = await manager.findOne(OrderInfoEntity, {
      where: { id: orderId },
    });
    if (!order || order.status != 0 || order.userId != userId) {
      throw new CoolCommException('订单不存在或不是可以支付的状态');
    }

    const user = await manager.findOne(UserInfoEntity, {
      where: { unionid: userId },
    });
    if (!user) {
      throw new CoolCommException('用户不存在');
    }

    const ratio = await this.getCarbonRatio();
    const payAmount = new BigNumber(order.price).minus(order.discountPrice || 0);
    const carbonCost = payAmount.multipliedBy(ratio).toNumber();

    if (Number(user.carbonBalance || 0) < carbonCost) {
      throw new CoolCommException('碳积分不足');
    }

    const dec = await manager
      .createQueryBuilder()
      .update(UserInfoEntity)
      .set({ carbonBalance: () => `carbonBalance - ${carbonCost}` })
      .where('id = :id AND carbonBalance >= :cost', {
        id: userId,
        cost: carbonCost,
      })
      .execute();
    if (!dec.affected) {
      throw new CoolCommException('碳积分不足');
    }

    await manager.update(OrderInfoEntity, order.id, {
      payType: 3,
      status: 1,
      payTime: new Date(),
    });

    await manager.save(RecycleTransactionEntity, {
      userId: user.unionid || String(userId),
      departmentId: user.departmentId,
      type: '支出',
      amount: carbonCost,
      sourceType: '商城',
      remark: `商城订单 ${order.orderNum} 碳积分支付`,
      orderId: order.orderNum,
    });

    const orderConfirm = await this.baseSysParamService.dataByKey('orderConfirm');
    this.orderQueue.add(
      { orderId: order.id, action: Action.CONFIRM },
      {
        delay: orderConfirm * 60 * 60 * 1000,
      }
    );

    return true;
  }
}
