package com.ruoyi.app.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ruoyi.app.order.ZaloPayCommonService;
import com.ruoyi.app.order.dto.OrderPushBodyDto;
import com.ruoyi.app.utils.*;
import com.ruoyi.app.utils.event.PushEventService;
import com.ruoyi.app.utils.zaloPay.*;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.system.domain.*;
import com.ruoyi.system.domain.vo.PayVN;
import com.ruoyi.system.mapper.RiderPositionMapper;
import com.ruoyi.system.service.*;
import com.ruoyi.system.utils.Auth;
import com.ruoyi.system.utils.JwtUtil;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import jakarta.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.io.IOException;
import java.net.URISyntaxException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * ZaloPay支付控制器
 **/
@RestController
@RequestMapping("/zaloPay")
public class ZaloPayController extends BaseController {
    private static final Logger log = LoggerFactory.getLogger(ZaloPayController.class);
    @Autowired
    private IPosOrderService posOrderService;
    @Autowired
    private IInfoUserService infoUserService;
    @Autowired
    private IUserMarginService userMarginService;
    @Autowired
    private IUserBillingService userBillingService;
    @Autowired
    private IIpnLogService ipnLogService;
    @Autowired
    private RiderPositionMapper riderPositionMapper;
    @Autowired
    private PayController payController;
    @Autowired
    private IPoseOrderZalopayService zalopayService;
    @Autowired
    private PushEventService pushEventService;
    private Mac HmacSHA256;

    @Autowired
    private ZaloPayCommonService zaloPayCommonService;
    @Autowired
    private ZaloPayConfig zaloPayConfig;
    @Autowired
    private ZaloPay zaloPay;

    public ZaloPayController() throws Exception  {
        HmacSHA256 = Mac.getInstance("HmacSHA256");

    }

    /**
     *  创建ZaloPay订单
     */

    @Anonymous
    @Auth
    @RepeatSubmit(interval = 1000, message = "请求过于频繁")
    @RequestMapping(value = "/ZaloPay", method = {RequestMethod.GET, RequestMethod.POST})
    public AjaxResult ZaloPay(@RequestParam(defaultValue = "") String language,
                              @RequestParam String orderId,
                              @RequestParam String amount,
                              @RequestParam String payType,
                              HttpServletRequest request) throws Exception {
        System.out.println("ZaloPay创建支付开始 orderId: " + orderId+ ", amount: " + amount);
        LambdaQueryWrapper<PosOrder> wrapper = new LambdaQueryWrapper<>();
        orderId=orderId.trim();
        wrapper.eq(PosOrder::getDdId, orderId);
        PosOrder posOrder = posOrderService.getOne(wrapper);
        if (posOrder == null) {
            System.out.println("no.order.id.error---order为null");
            return error(MessageUtils.message("no.order.id.error"));
        }
        PoseOrderZalopay zaloResopen = zalopayService.getOne(new LambdaQueryWrapper<PoseOrderZalopay>().eq(PoseOrderZalopay::getDdId, orderId));
        if(zaloResopen!=null && zaloResopen.getCreateResponse()!=null && zaloResopen.getCreateResponseTime()!=null){
            long currentTimeMillis = System.currentTimeMillis();
            long createResponseTimeMillis = zaloResopen.getCreateResponseTime().getTime();
            long diffMinutes = (currentTimeMillis - createResponseTimeMillis) / (1000 * 60);
            //ordertoken有效时间15分钟
            if(diffMinutes<=14){
                //该处计算当前时间比较创建时间，如果大于10分钟则认为订单已支付
                return success(MessageUtils.message("no.order.create.success"), JSON.parseObject(zaloResopen.getCreateResponse(),ZaloPayCreateOrderResponse.class));
            }
        }
        ZaloPayCreateOrderResponse response;
        ZaloPayProperties.ZaloPayConfig config = zaloPayConfig.getZaloPayConfig(payType);
        try {
            response = zaloPay.createOrder(String.valueOf(posOrder.getUserId()), orderId, Long.parseLong(amount), "City Express-#" + posOrder.getDdId(),config);

        } catch (Exception e) {
            System.out.println("no.order.id.error---抛出异常");
            // 记录详细错误信息以便调试
            log.error("ZaloPay创建失败 orderId: {}, error: {}", orderId, e.getMessage(), e);
            System.out.println("ZaloPay创建失败 orderId: " + orderId + ", error: " + e.getMessage());
            return error(MessageUtils.message("no.order.id.error"));
        }
        if(response.getReturnCode()==2){
            return error(response.getReturnMessage());
        }
        zaloPayCommonService.setZaloPayOrder(orderId,JSON.toJSONString(response),null,null,null,payType);
        System.out.println("ZaloPay创建支付成功");
        PosOrder pos = new PosOrder();
        pos.setId(posOrder.getId());
        pos.setPayType(payType);
        posOrderService.saveOrUpdate(pos);
        return success(MessageUtils.message("no.order.create.success"), response);
    }


    //查询支付订单

    @SneakyThrows
    @Anonymous
    @RequestMapping(value = "/getZaloPayorder", method = {RequestMethod.GET})
    public AjaxResult getZaloPayorder(@RequestParam String ddId) {
        PoseOrderZalopay zaloResopen = zalopayService.getOne(new QueryWrapper<PoseOrderZalopay>().eq("dd_id", ddId));
        if(zaloResopen==null){
            return error("未查到支付信息");
        }
        Date createResponseTime = zaloResopen.getCreateResponseTime();
        SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd");
        String formattedDate = sdf.format(createResponseTime);
        ZaloPayProperties.ZaloPayConfig config = zaloPayConfig.getZaloPayConfig(zaloResopen.getPayType());
        ZaloPayQueryOrderResponse response = zaloPay.queryOrderStatus(formattedDate+"_" +ddId,config);
        return success(response);
    }

    //zalopay退款请求（申请退款同意退款后可触发退款）
    //客户能发起退款（如果成功），金额原路返回给用户，商家和骑手的金额都不会影响。而客户投诉会当个别订单处理。
    @Anonymous
    @SneakyThrows
    @Auth
    @RepeatSubmit(interval = 1000, message = "请求过于频繁")
    @RequestMapping(value = "/zaloRefund", method = {RequestMethod.GET, RequestMethod.POST})
    public AjaxResult zaloRefund(@RequestHeader String token,
                                 @RequestBody PayVN payVN,
                                 HttpServletRequest request) {

        JwtUtil jwtUtil = new JwtUtil();
        IpUtils ipUtils = new IpUtils();
        String ip = ipUtils.getIpAddr(request);
        String id = jwtUtil.getusid(token);
        InfoUser user = infoUserService.getById(id);
        System.out.println("退款用户信息：" + user.getPhone());
        PosOrder posOrder = posOrderService.getOne(new LambdaQueryWrapper<PosOrder>().eq(PosOrder::getDdId, payVN.getOrder_id()).eq(PosOrder::getUserId, id));
        if(posOrder==null){
            return error("订单不存在");
        }
        PoseOrderZalopay zaloResopen = zalopayService.getOne(new QueryWrapper<PoseOrderZalopay>().eq("dd_id", payVN.getOrder_id()));
        if(zaloResopen==null) return error("支付信息不存在");
        JSONObject cbdata = JSON.parseObject(zaloResopen.getCallbackResponse());
        String zpTransId = cbdata.getString("zp_trans_id");
        String payType=zaloResopen.getPayType();
        ZaloPayProperties.ZaloPayConfig config = zaloPayConfig.getZaloPayConfig(payType);
        String APP_ID = config.getApp_id();
        String mRefundId = ZaloPay.getCurrentTimeString("yyMMdd")+"_" +APP_ID+ "_" + payVN.getOrder_id();
        ZaloPayRefundResponse response = zaloPay.Refund(zpTransId,Long.parseLong(payVN.getAmount()),mRefundId,config);
        System.out.println("退款返回：" +JSON.toJSONString(response));
        QueryWrapper<UserBilling> wrapper = new QueryWrapper<>();
        wrapper.eq("dd_id", payVN.getOrder_id());
        wrapper.eq("user_id", id);
        wrapper.eq("type", "3");  //消费
        wrapper.eq("state", "0"); // 0完成
        UserBilling billing = userBillingService.getOne(wrapper);
        System.out.println("退款结果代码：" + response.getReturnCode());
        zaloPayCommonService.setZaloPayOrder(payVN.getOrder_id(), null, null, JSON.toJSONString(response),mRefundId,null);
        if (response.getReturnCode().equals(1)) {
            setRefoudBilling(billing, id, payVN);
            return success(response);
        }else if(response.getReturnCode().equals(3)){
            System.out.println("退款RetrunCode为3，退款正在处理中，触发查询退款状态");
            ZaloPayProperties.ZaloPayConfig conf = zaloPayConfig.getZaloPayConfig(payType);
            ZaloPayQueryRefundResponse queryResponse = zaloPay.queryRefundStatus(mRefundId, conf);
            System.out.println("退款查询：" +JSON.toJSONString(queryResponse));
            if(queryResponse.getReturnCode()==1)
            {
                setRefoudBilling(billing, id, payVN);
                System.out.println("退款查询成功设置退款成功");
                response.setReturnCode(1);
                return success(response);
            }
            System.out.println("退款查询未设置退款成功");
            return success(response);
        }
        else {
            return success(response);
        }
    }



    private void cancelOrderSendShMessage(PosOrder order){
        PayPush push = new PayPush();

    }

    //退款设置账单
    private void setRefoudBilling(UserBilling billing, String id, PayVN payVN)
    {
        if (payVN.getTrantype().equals("02")) {
            billing.setState("2");  //作废
            userBillingService.saveOrUpdate(billing);
        } else {
            //场景没有骑手接单，设置为退回
            UserBilling usbi = new UserBilling();
            usbi.setUserId(Long.valueOf(id));
            usbi.setType("3");  //消费
            usbi.setDdId(payVN.getOrder_id());
            usbi.setAmount(Double.valueOf(payVN.getAmount()));
            usbi.setMdId(billing.getMdId());
            usbi.setIllustrate(payVN.getIllustrate());
            usbi.setState("1");  //退回
            userBillingService.saveOrUpdate(usbi);
        }
    }



    /**
     * 查询退款状态
     * @param ddId
     * @return
     */
    @SneakyThrows
    @Anonymous
    @RequestMapping(value = "/queryRefundStatus", method = {RequestMethod.GET})
    public AjaxResult queryRefundStatus(String ddId) throws URISyntaxException, IOException {
        PoseOrderZalopay zaloResopen = zalopayService.getOne(new QueryWrapper<PoseOrderZalopay>().eq("dd_id", ddId));
        if(zaloResopen==null){
            return error("未查到支付信息");
        }
        String mRefundId = zaloResopen.getMRefundId();
        ZaloPayProperties.ZaloPayConfig config = zaloPayConfig.getZaloPayConfig(zaloResopen.getPayType());
        ZaloPayQueryRefundResponse response = zaloPay.queryRefundStatus(mRefundId,config);
        return success(response);
    }




    /**
     * ZaloPay 支付成功之后回调接口
     * @param jsonStr
     * @return
     */
    @Anonymous
    @RequestMapping(value = "/callback", method = {RequestMethod.POST})
    public String  callback(@RequestBody String jsonStr,HttpServletRequest request) {
        System.out.println("ZaloPay回调信息" + jsonStr);
        IpUtils ipUtils = new IpUtils();
        String ip = ipUtils.getIpAddr(request);
        IpnLog log = new IpnLog();
        log.setIp(ip);
        log.setIpnLog(jsonStr);
        ipnLogService.insertIpnLog(log);
        JSONObject result = new JSONObject();
        try {
            JSONObject cbdata = JSON.parseObject(jsonStr);
            String dataStr = cbdata.getString("data");
            String reqMac = cbdata.getString("mac");
            JSONObject data = JSON.parseObject(dataStr);
            //订单id
            String appTransId ="";
            if(data.getString("app_trans_id")!=null){
                appTransId = data.getString("app_trans_id");
            }else{
                appTransId = data.getString("apptransid");
            }
            String ddId = appTransId.substring(7);
            PoseOrderZalopay poseOrderZalopay =zalopayService.getOne(new QueryWrapper<PoseOrderZalopay>().eq("dd_id", ddId));
            String payType = poseOrderZalopay.getPayType();
            ZaloPayProperties.ZaloPayConfig config = zaloPayConfig.getZaloPayConfig(payType);
            logger.info("zalopay回调获取到的zalopayConfig",JSON.toJSONString(config));
            String key2 = config.getKey2();
            HmacSHA256.init(new SecretKeySpec(key2.getBytes(), "HmacSHA256"));
            byte[] hashBytes = HmacSHA256.doFinal(dataStr.getBytes());
            String mac = DatatypeConverter.printHexBinary(hashBytes).toLowerCase();
            zaloPayCommonService.setZaloPayOrder(ddId,null,dataStr,null,null,null);
            // check if the callback is valid (from ZaloPay server)
            if (!reqMac.equals(mac)) {
                // callback is invalid
                result.put("return_code", -1);
                result.put("return_message", "mac not equal");
            } else {
                PayPush push = new PayPush();
                // payment success
                // merchant update status for order's status

                String orderType = ddId.substring(0, 2);
                //订单为充值类型
                if (orderType.equals("CZ")) {
                    rechargeCallback(result, ddId, data, push);
                }
                else {
                    orderCallback(result, ddId, data, push);
                }

            }
        } catch (Exception ex) {
            result.put("return_code", 0); // callback again (up to 3 times)
            result.put("return_message", ex.getMessage());
        }

        // returns the result for ZaloPay server
        return result.toString();
    }
    //订单回调处理
    private void orderCallback(JSONObject result,String ddId,JSONObject data,PayPush  push){
        QueryWrapper<PosOrder> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("dd_id", ddId);
        PosOrder posOrder = posOrderService.getOne(queryWrapper);
        if (posOrder == null) {
            System.out.println("订单不存在============");
            // 订单不存在
            result.put("return_code", 0);
            result.put("return_message", "success");
        } else {
            InfoUser user = infoUserService.getById(posOrder.getUserId());
            InfoUser shanghu = infoUserService.getById(posOrder.getShId());
            Integer amount =  data.getInteger("amount");
            Integer oramout = posOrder.getAmount();
            System.out.println("金额对比：" + amount + "/" + oramout);
            if (amount.equals(oramout)) {
                System.out.println("订单状态：" + posOrder.getState());
                if (posOrder.getState() == 0) {
                    PosOrder order = new PosOrder();
                    order.setId(posOrder.getId());
                    order.setState(1L);
                    posOrderService.saveOrUpdate(order);
                    QueryWrapper<UserBilling> wrapper = new QueryWrapper<>();
                    wrapper.eq("dd_id", posOrder.getDdId());
                    wrapper.eq("type", "3");
                    wrapper.eq("state", "3");
                    UserBilling billing = userBillingService.getOne(wrapper);
                    if (billing != null) {
                        billing.setState("0");
                        billing.setAmount(Double.valueOf(amount));
                        billing.setIllustrate("ZaloPayConsumer payment");
                        billing.setPaymentMethod("ZaloPay");
                        userBillingService.saveOrUpdate(billing);
                    }
                    System.out.println("======================================推送支付结果");
                    push.apppush(
                            user.getCid(),
                            MessageUtils.message("no.message.push.message"),
                            MessageUtils.message("no.message.push.payment.success"),
                            OrderPushBodyDto.getJson(String.valueOf(posOrder.getDdId()), String.valueOf(order.getState()),0)
                    );
                    pushEventService.PublisherEvent(user.getUserId(), MessageUtils.message("no.message.push.message"), MessageUtils.message("no.message.push.payment.success"), OrderPushBodyDto.getJson(String.valueOf(posOrder.getDdId()), String.valueOf(order.getState()),0));
                    push.shpush(
                            shanghu.getCid(),
                            MessageUtils.message("no.message.push.message"),
                            MessageUtils.message("no.message.push.new.order"),
                            OrderPushBodyDto.getJson(String.valueOf(posOrder.getDdId()), String.valueOf(order.getState()),0)
                    );
                    pushEventService.PublisherEvent(shanghu.getUserId(), MessageUtils.message("no.message.push.message"), MessageUtils.message("no.message.push.new.order"), OrderPushBodyDto.getJson(String.valueOf(posOrder.getDdId()), String.valueOf(order.getState()),0));
                    payController.sendAcceptRiderPush(posOrder, push, riderPositionMapper,OrderPushBodyDto.getJson(String.valueOf(posOrder.getDdId()), String.valueOf(order.getState()),0));
                    result.put("return_code", 1);
                    result.put("return_message", "success");
                } else {
                    // 3. 订单已确认 - 返回成功响应
                    result.put("return_code", 1);
                    result.put("return_message", "success");
                }
            } else {
                System.out.println("订单金额对不上");
                //订单金额对不上
                result.put("return_code", 1001);
                result.put("return_message", "Order amount mismatch");
            }
        }
    }

    //充值回调处理
    private void rechargeCallback(JSONObject result,String ddId,JSONObject data,PayPush  push) {
        logger.info("充值订单");
        QueryWrapper<UserBilling> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("dd_id",ddId);
        UserBilling billing = userBillingService.getOne(queryWrapper);
        if (billing == null) {
            logger.info("充值订单--订单不存在");
            // 订单不存在
            result.put("return_code", 0);
            result.put("return_message", "success");
        } else {
            InfoUser user = infoUserService.getById(billing.getUserId());
            Integer amount =  data.getInteger("amount");
            Double oramout = billing.getAmount();
            DecimalFormat df = new DecimalFormat("0.##");
//                        String s = df.format(oramout);
            logger.info("充值订单--金额对比：{}/{}", amount, oramout);
            System.out.println("金额对比：" + amount + "/" + oramout);
            if (Double.valueOf(amount).equals(oramout)) {
                if (billing.getState().equals("3")) {
                    UserBilling usbi = new UserBilling();
                    usbi.setId(billing.getId());
                    usbi.setState(String.valueOf(0));
                    userBillingService.saveOrUpdate(usbi);
                    String json=OrderPushBodyDto.getJson(billing.getDdId(),billing.getState(),-1,2);
                    if (user.getUserType().equals("1")) {
                        push.shpush(
                                user.getCid(),
                                MessageUtils.message("no.message.push.message"),
                                MessageUtils.message("no.message.push.recharge.success"),
                                json
                        );
                        result.put("return_code", 1);
                        result.put("return_message", "success");
                        logger.info("充值订单--回调成功");
                    } else {
                        push.qspush(
                                user.getCid(),
                                MessageUtils.message("no.message.push.message"),
                                MessageUtils.message("no.message.push.recharge.success"),
                                json
                        );
                        result.put("return_code", 1);
                        result.put("return_message", "success");
                        logger.info("充值订单--回调成功");
                    }
                } else {
                    // 3. 订单已确认 - 返回成功响应
                    result.put("return_code", 1);
                    result.put("return_message", "success");
                    logger.info("充值订单--回调成功");
                }
            } else {
                //订单金额对不上
                result.put("return_code", 1001);
                result.put("return_message", "Order amount mismatch");
                logger.info("充值订单--订单金额对不上");

            }
        }

    }


}
