package com.gxwebsoft.shop.controller;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayConstants;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipayEncrypt;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradeCreateRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradeCreateResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.gxwebsoft.apps.entity.BcTemporary;
import com.gxwebsoft.apps.param.BcTemporaryParam;
import com.gxwebsoft.apps.service.BcTemporaryService;
import com.gxwebsoft.common.core.annotation.OperationLog;
import com.gxwebsoft.common.core.config.ConfigProperties;
import com.gxwebsoft.common.core.exception.BusinessException;
import com.gxwebsoft.common.core.utils.AlipayConfigUtil;
import com.gxwebsoft.common.core.web.*;
import com.gxwebsoft.common.system.entity.User;
import com.gxwebsoft.common.system.service.OperationRecordService;
import com.gxwebsoft.common.system.service.UserService;
import com.gxwebsoft.shop.entity.Order;
import com.gxwebsoft.shop.entity.Payment;
import com.gxwebsoft.shop.entity.UserBalanceLog;
import com.gxwebsoft.shop.param.PaymentParam;
import com.gxwebsoft.shop.service.OrderService;
import com.gxwebsoft.shop.service.PaymentService;
import com.gxwebsoft.shop.service.UserBalanceLogService;
import com.gxwebsoft.shop.service.UserOauthService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import org.springframework.core.env.Environment;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.gxwebsoft.common.core.constants.BalanceConstants.BALANCE_USE;
import static com.gxwebsoft.common.core.constants.OrderConstants.*;

/**
 * 商城支付方式记录表控制器
 *
 * @author 科技小王子
 * @since 2022-12-15 19:11:07
 */
@Api(tags = "商城支付方式记录表管理")
@RestController
@RequestMapping("/api/shop/payment")
public class PaymentController extends BaseController {
  @Resource
  private PaymentService paymentService;
  @Resource
  private OrderService orderService;
  @Resource
  private Environment config;
  @Resource
  private ConfigProperties pathConfig;
  @Resource
  private OperationRecordService operationRecordService;
  @Resource
  private UserOauthService userOauthService;
  @Resource
  private AlipayConfigUtil alipayConfig;
  @Resource
  private UserService userService;
  @Resource
  private UserBalanceLogService userBalanceLogService;
  @Resource
  private BcTemporaryService bcTemporaryService;

  @PreAuthorize("hasAuthority('shop:payment:list')")
  @ApiOperation("分页查询商城支付方式记录表")
  @GetMapping("/page")
  public ApiResult<PageResult<Payment>> page(PaymentParam param) {
    PageParam<Payment, PaymentParam> page = new PageParam<>(param);
    return success(paymentService.page(page, page.getWrapper()));
    // 使用关联查询
    //return success(paymentService.pageRel(param));
  }

  @PreAuthorize("hasAuthority('shop:payment:list')")
  @ApiOperation("查询全部商城支付方式记录表")
  @GetMapping()
  public ApiResult<List<Payment>> list(PaymentParam param) {
    PageParam<Payment, PaymentParam> page = new PageParam<>(param);
    page.setDefaultOrder("create_time desc");
    return success(paymentService.list(page.getOrderWrapper()));
    // 使用关联查询
    //return success(paymentService.listRel(param));
  }

  @PreAuthorize("hasAuthority('shop:payment:list')")
  @ApiOperation("根据id查询商城支付方式记录表")
  @GetMapping("/{id}")
  public ApiResult<Payment> get(@PathVariable("id") Integer id) {
    return success(paymentService.getById(id));
    // 使用关联查询
    //return success(paymentService.getByIdRel(id));
  }

  @PreAuthorize("hasAuthority('shop:payment:save')")
  @ApiOperation("添加商城支付方式记录表")
  @PostMapping()
  public ApiResult<?> save(@RequestBody Payment payment) {
    if (paymentService.count(new LambdaQueryWrapper<Payment>()
      .eq(Payment::getMethod, payment.getMethod())) > 0) {
      return fail("该支付方式已存在");
    }
    if (paymentService.save(payment)) {
      return success("添加成功");
    }
    return fail("添加失败");
  }

  @PreAuthorize("hasAuthority('shop:payment:update')")
  @OperationLog
  @ApiOperation("修改商城支付方式记录表")
  @PutMapping()
  public ApiResult<?> update(@RequestBody Payment payment) {
    if (paymentService.updateById(payment)) {
      return success("修改成功");
    }
    return fail("修改失败");
  }

  @PreAuthorize("hasAuthority('shop:payment:remove')")
  @OperationLog
  @ApiOperation("删除商城支付方式记录表")
  @DeleteMapping("/{id}")
  public ApiResult<?> remove(@PathVariable("id") Integer id) {
    if (paymentService.removeById(id)) {
      return success("删除成功");
    }
    return fail("删除失败");
  }

  @PreAuthorize("hasAuthority('shop:payment:save')")
  @OperationLog
  @ApiOperation("批量添加商城支付方式记录表")
  @PostMapping("/batch")
  public ApiResult<?> saveBatch(@RequestBody List<Payment> list) {
    if (paymentService.saveBatch(list)) {
      return success("添加成功");
    }
    return fail("添加失败");
  }

  @PreAuthorize("hasAuthority('shop:payment:update')")
  @OperationLog
  @ApiOperation("批量修改商城支付方式记录表")
  @PutMapping("/batch")
  public ApiResult<?> removeBatch(@RequestBody BatchParam<Payment> batchParam) {
    if (batchParam.update(paymentService, "payment_id")) {
      return success("修改成功");
    }
    return fail("修改失败");
  }

  @PreAuthorize("hasAuthority('shop:payment:remove')")
  @OperationLog
  @ApiOperation("批量删除商城支付方式记录表")
  @DeleteMapping("/batch")
  public ApiResult<?> removeBatch(@RequestBody List<Integer> ids) {
    if (paymentService.removeByIds(ids)) {
      return success("删除成功");
    }
    return fail("删除失败");
  }

  @ApiOperation("支付宝手机号码")
  @PostMapping("/getPhoneNumber")
  public ApiResult<?> getPhoneNumber(@RequestBody Map<String, String> params) {
    final String encryptedData = params.get("encryptedData");
    final String tenantId = params.get("tenantId");
    // 支付宝配置信息
    JSONObject config = alipayConfig.payment(Integer.valueOf(tenantId));

    //1. 获取验签和解密所需要的参数
    JSONObject jsonObject =JSONObject.parseObject(encryptedData);
    String content = jsonObject.getString("response");
    String sign = jsonObject.getString("sign");
    String signType = "RSA2";
    String charset = "UTF-8";
    String encryptType = "AES";
    String alipayCertPublicKey = pathConfig.getUploadPath() + "file" + config.getString("alipayCertPublicKey");
    boolean isDataEncrypted = !content.startsWith("{");
    boolean signVerified = false;
    //2. 验签
    String signContent = content;
    String signVeriKey = ""; // 支付宝公钥
    String decryptKey = config.getString("decryptKey"); // 加解密密钥
//    System.out.println("decryptKey = " + decryptKey);
//    System.out.println(content);
//    System.out.println(sign);
//    System.out.println(alipayCertPublicKey);
//    System.out.println(charset);
//    System.out.println(signType);
//    System.out.println(isDataEncrypted);
    if (isDataEncrypted) {
      signContent = "\"" + signContent + "\"";
    } try {
      //验签方法
      signVerified = AlipaySignature.rsaCertCheck(signContent, sign, alipayCertPublicKey, charset, signType);
    } catch (AlipayApiException e) {
      // 验签异常, 日志
    } if (!signVerified) {
      //验签不通过（异常或者报文被篡改），终止流程（不需要做解密）
      return fail("验签失败");
    }
    //3. 解密
    String plainData = null;
    if (isDataEncrypted) {
      try {
        plainData = AlipayEncrypt.decryptContent(content, encryptType, decryptKey, charset);
      } catch (AlipayApiException e) {
        //解密异常, 记录日志
        return fail("解密异常");
      }} else {
      plainData = content;
    }
    return success("获取成功",plainData);
  }

  @ApiModelProperty("支付宝小程序支付")
  @GetMapping("/mp-alipay/{id}")
  public ApiResult<?> mpAlipay(@PathVariable("id") Integer id) throws AlipayApiException {
    // 验证签名
    isCheckSign();
    // 订单数据
    Order order = orderService.getByIdRel(id);
    // 实例化客户端
    DefaultAlipayClient alipayClient = alipayConfig.alipayClient(order.getTenantId());
    try {
      AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
      // 配置公共请求参数
      request.setNotifyUrl(pathConfig.getServerUrl() + "/shop/payment/mp-alipay/notify");
//      request.setNotifyUrl("https://454k72r798.goho.co/api/shop/payment/mp-alipay/notify");
      request.setReturnUrl(null);
      // 配置业务参数
      JSONObject bizContent = new JSONObject();
      System.out.println("bizContent = " + order);
      bizContent.put("out_trade_no", order.getOrderNo());
      bizContent.put("total_amount", order.getPayPrice());
      bizContent.put("subject", order.getMerchantName());
      // 拿不到手机号码？？
      bizContent.put("buyer_id", userOauthService.getOauthIdByUserId(order.getUserId(), "MP-ALIPAY"));
      request.setBizContent(bizContent.toString());
      //SDK 已经封装掉了公共参数，这里只需要传入业务参数。
      AlipayTradeCreateResponse response = alipayClient.certificateExecute(request);
      String trade_no = response.getTradeNo();// 获取返回的tradeNO。
      return success("支付成功", trade_no);
    } catch (AlipayApiException e) {
      e.printStackTrace();
      throw new RuntimeException();
    }
  }

  @ApiModelProperty("异步通知")
  @OperationLog
  @PostMapping("/mp-alipay/notify")
  public String alipayNotify(@RequestParam Map<String, String> params) throws AlipayApiException {
    System.out.println("异步处理>>>>");
    System.out.println("params = " + params);
    String outTradeNo = params.get("out_trade_no");
    Order order = orderService.getByOutTradeNo(outTradeNo);
    if(order == null){
      throw new BusinessException("订单不存在");
    }
    final JSONObject config = alipayConfig.payment(order.getTenantId());
    // 生成环境证书路径
    String alipayCertPublicKey = pathConfig.getUploadPath() + "file" + config.getString("alipayCertPublicKey");
    // TODO 验签成功后，按照支付结果异步通知中的描述，对支付结果中的业务内容进行二次校验，校验成功后在response中返回success并继续商户自身业务处理，校验失败返回failure
    boolean flag = AlipaySignature.rsaCertCheckV1(params, alipayCertPublicKey, AlipayConstants.CHARSET_UTF8, AlipayConstants.SIGN_TYPE_RSA2);
    System.out.println("flag>>>>>>>>>>>>>>>>>>>>>>>");
    System.out.println(flag);
    // 处理订单业务
    if (flag) {
      final String tradeStatus = params.get("trade_status");
      final String receipt_amount = params.get("receipt_amount");
      final String payPrice = order.getPayPrice().toString();
      final String trade_no = params.get("trade_no");
      final String subject = params.get("subject");

      // 1. 验证appId是否一致
      final String app_id = params.get("app_id");
      if(!config.getString("alipayAppId").equals(app_id)){
        System.out.println("支付宝appId不一致 = " + app_id);
        throw new BusinessException("支付宝appId不一致");
      }
      // 2. 订单金额
      if(!payPrice.equals(receipt_amount)){
        System.out.println("订单金额是不一致 = " + receipt_amount);
        throw new BusinessException("订单金额是不一致");
      }
      // 3. 判断交易状态
      if(!"TRADE_SUCCESS".equals(tradeStatus)){
        System.out.println("支付失败 = " + tradeStatus);
        throw new BusinessException("支付失败");
      }
      // 4. 修改支付状态
      order.setPayStatus(PAY_STATUS_SUCCESS);
      order.setPayMethod(PAY_METHOD_ALIPAY);
      order.setReceiptAmount(new BigDecimal(receipt_amount));
      order.setPayTime(DateUtil.date());
      order.setTradeId(trade_no);
      order.setSubject(subject);
      System.out.println("order2 = " + order);
      final boolean b = orderService.updateByIdRel(order);
      System.out.println("bsss = " + b);
      return "success";
    }
    // TODO 验签失败则记录异常日志，并在response中返回failure.
    return "failure";
  }

  @OperationLog
  @ApiModelProperty("余额支付")
  @GetMapping("/balance/{id}")
  @Transactional(rollbackFor = {Exception.class})
  public ApiResult<?> balance(@PathVariable("id") Integer id) throws AlipayApiException {
    // 1. 验证签名
    isCheckSign();
    // 订单数据
    Order order = orderService.getByIdRel(id);
    // 当前登录用户id
    User user = new User();
    // 代付款情况
    if(!order.getUserId().equals(getLoginUserId())){
       user = userService.getById(order.getUserId());
    }else{
       user = getLoginUser();
    }
    final Integer userId = user.getUserId();
    final BigDecimal balance = user.getBalance();
    final BigDecimal payPrice = order.getPayPrice();
    if(balance.compareTo(payPrice) < 0){
      return fail("余额不足 = " + balance.compareTo(payPrice));
    }
    // 2. 扣除余额操作
    BigDecimal subtract = balance.subtract(payPrice);
    user.setBalance(subtract);
    userService.updateById(user);
    // 3. 记录余额明细
    UserBalanceLog userBalanceLog = new UserBalanceLog();
    userBalanceLog.setUserId(userId);
    userBalanceLog.setScene(BALANCE_USE);
    userBalanceLog.setMoney(payPrice);
    userBalanceLog.setBalance(subtract);
    userBalanceLog.setComments(order.getOrderNo().toString());
    userBalanceLog.setMerchantCode(order.getMerchantCode());
    userBalanceLogService.save(userBalanceLog);
    // 4. 修改支付状态
    order.setPayStatus(PAY_STATUS_SUCCESS);
    order.setPayMethod(PAY_METHOD_BALANCE);
    order.setReceiptAmount(payPrice);
    order.setPayTime(DateUtil.date());
    order.setSubject(order.getMerchantName());
    orderService.updateByIdRel(order);
    // 5. 续租订单
    if(order.getRentOrderId() > 0){
      // 主订单
      Order parentOrder = orderService.getById(order.getRentOrderId());
      // 更新过期时间延长一个月
      Date expirationTime = parentOrder.getExpirationTime();
      DateTime nextMonthTime = DateUtil.offsetMonth(expirationTime, 1);
      parentOrder.setExpirationTime(nextMonthTime);
      orderService.updateById(parentOrder);
      // 保存续费订单状态
      order.setDeliveryStatus(DELIVERY_STATUS_YES);
      order.setReceiptStatus(RECEIPT_STATUS_YES);
      order.setOrderStatus(ORDER_STATUS_COMPLETED);
      order.setStartTime(expirationTime);
      order.setExpirationTime(nextMonthTime);
      orderService.updateById(order);
    }
    // 6. 是否是临时报餐
    final BcTemporaryParam bcTemporaryParam = new BcTemporaryParam();
    bcTemporaryParam.setUserId(getLoginUserId());
    bcTemporaryParam.setApplyStatus(1);
    final List<BcTemporary> bcTemporaries = bcTemporaryService.listRel(bcTemporaryParam);
    bcTemporaries.forEach(b -> {
      b.setStatus(1);
      bcTemporaryService.updateById(b);
    });
    return success("支付成功",user);
  }

  @OperationLog
  @ApiModelProperty("余额支付批量")
  @PostMapping("/balanceBatch")
  @Transactional(rollbackFor = {Exception.class})
  public ApiResult<?> balanceBatch(@RequestBody List<Integer> orderIds) throws AlipayApiException {
    // 1. 验证签名
    isCheckSign();
    // 订单数据
    final List<Order> list = orderService.listByIds(orderIds);
    final StringBuilder sb = new StringBuilder();

    list.forEach(d -> {
      // 当前登录用户id
      User user = new User();
      // 代付款情况
      if(!d.getUserId().equals(getLoginUserId())){
        user = userService.getById(d.getUserId());
      }else{
        user = getLoginUser();
      }
      final Integer userId = user.getUserId();
      final BigDecimal balance = user.getBalance();
      final BigDecimal payPrice = d.getPayPrice();
      if(balance.compareTo(payPrice) < 0){
        sb.append("下单失败：").append(user.getNickname()).append("余额不足");
        throw new BusinessException("余额不足");
      }
      // 2. 扣除余额操作
      BigDecimal subtract = balance.subtract(payPrice);
      user.setBalance(subtract);
      userService.updateById(user);
      // 3. 记录余额明细
      UserBalanceLog userBalanceLog = new UserBalanceLog();
      userBalanceLog.setUserId(userId);
      userBalanceLog.setScene(BALANCE_USE);
      userBalanceLog.setMoney(payPrice);
      userBalanceLog.setBalance(subtract);
      userBalanceLog.setComments(d.getOrderNo().toString());
      userBalanceLog.setMerchantCode(d.getMerchantCode());
      userBalanceLogService.save(userBalanceLog);
      // 4. 修改支付状态
      d.setPayStatus(PAY_STATUS_SUCCESS);
      d.setPayMethod(PAY_METHOD_BALANCE);
      d.setReceiptAmount(payPrice);
      d.setPayTime(DateUtil.date());
      d.setSubject(d.getMerchantName());
      orderService.updateByIdRel(d);
      // 5. 续租订单
      if(d.getRentOrderId() > 0){
        // 主订单
        Order parentOrder = orderService.getById(d.getRentOrderId());
        // 更新过期时间延长一个月
        Date expirationTime = parentOrder.getExpirationTime();
        DateTime nextMonthTime = DateUtil.offsetMonth(expirationTime, 1);
        parentOrder.setExpirationTime(nextMonthTime);
        orderService.updateById(parentOrder);
        // 保存续费订单状态
        d.setDeliveryStatus(DELIVERY_STATUS_YES);
        d.setReceiptStatus(RECEIPT_STATUS_YES);
        d.setOrderStatus(ORDER_STATUS_COMPLETED);
        d.setStartTime(expirationTime);
        d.setExpirationTime(nextMonthTime);
        orderService.updateById(d);
      }
      // 6. 是否是临时报餐
      final BcTemporaryParam bcTemporaryParam = new BcTemporaryParam();
      bcTemporaryParam.setUserId(getLoginUserId());
      bcTemporaryParam.setApplyStatus(1);
      final List<BcTemporary> bcTemporaries = bcTemporaryService.listRel(bcTemporaryParam);
      bcTemporaries.forEach(b -> {
        b.setStatus(1);
        bcTemporaryService.updateById(b);
      });
    });
    return success("支付成功",sb);
  }

  @ApiModelProperty("统一收单交易查询")
  @GetMapping("/mp-alipay/query/{id}")
  public ApiResult<?> query(@PathVariable("id") Integer id) throws AlipayApiException {
    // 验证签名
    isCheckSign();
    // 订单数据
    Order order = orderService.getByIdRel(id);
    // 实例化客户端
    DefaultAlipayClient alipayClient = alipayConfig.alipayClient(order.getTenantId());
    AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
    JSONObject bizContent = new JSONObject();
    bizContent.put("out_trade_no", order.getOrderNo());
    request.setBizContent(bizContent.toString());
    AlipayTradeQueryResponse response = alipayClient.certificateExecute(request);
    if(response.isSuccess()){
      System.out.println("调用成功");
      orderService.paySuccess(response);
    } else {
      System.out.println("调用失败");
    }
    return success("调用成功",response);
  }

  @ApiModelProperty("测试")
  @GetMapping("/mp-alipay/test")
  public String test() {
    System.out.println("开始>>>>>");
    Order order = orderService.getByOutTradeNo("2023213225911618");
    System.out.println("order = " + order);
    order.setPayPrice(new BigDecimal("0.11"));
    order.setTotalPrice(new BigDecimal("0.11"));
    order.setPayStatus(PAY_STATUS_SUCCESS);
    final boolean b = orderService.updateByIdRel(order);
    System.out.println("b = " + b);

//    params.put("gmt_create", "2022-12-16 21:32:33");
//    params.put("charset", "UTF-8");
//    params.put("seller_email", "zhu115289@163.com");
//    params.put("subject", "测试商品");
//    params.put("buyer_id", "2088202959044205");
//    params.put("invoice_amount", "0.01");
//    params.put("notify_id", "2022121601222213308044201435873270");
//    params.put("fund_bill_list", "");
//    params.put("notify_type", "trade_status_sync");
//    params.put("trade_status", "TRADE_SUCCESS");
//    params.put("receipt_amount", "0.01");
//    params.put("buyer_pay_amount", "0.01");
//    params.put("app_id", "2021003156628508");
//    params.put("seller_id", "2088431805524422");
//    params.put("gmt_payment", "2022-12-16 21:33:07");
//    params.put("notify_time", "2022-12-16 21:33:09");
//    params.put("version", "1.0");
//    params.put("out_trade_no", "20221116205303366");
//    params.put("total_amount", "0.01");
//    params.put("trade_no", "2022121622001444201411215463");
//    params.put("auth_app_id", "2021003156628508");
//    params.put("buyer_logon_id", "ip1***@163.com");
//    params.put("point_amount", "0.00");
//    // 处理订单业务
//    orderService.paySuccess(params);
    return "success";
  }
}
