package com.cf.imes.module.system.service.pay.factory.impl.alipay;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.Method;
import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayApiException;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.domain.AlipayTradePagePayModel;
import com.alipay.api.domain.AlipayTradeQueryModel;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.request.AlipayTradeQueryRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.alipay.api.response.AlipayTradeQueryResponse;
import com.cf.imes.framework.common.util.object.ObjectUtils;
import com.cf.imes.framework.pay.config.ChenfengAlipayConfig;
import com.cf.imes.framework.pay.core.client.dto.order.PayOrderRespDTO;
import com.cf.imes.module.system.controller.admin.pay.vo.PayOrderUnifiedReqDTO;
import com.cf.imes.framework.pay.core.enums.order.PayOrderStatusRespEnum;
import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum;
import com.cf.imes.module.system.enums.pay.PayConstants;
import com.cf.imes.module.system.service.pay.factory.impl.AbstractPayHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import jakarta.annotation.Resource;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;

/**
 * 支付宝电脑支付处理器
 *
 * @author Gqr
 * @since 2025/4/15 15:46
 */
@Slf4j
@Service("alipayPcPayHandler")
public class AlipayPcPayHandler extends AbstractPayHandler {

    @Resource
    private ChenfengAlipayConfig alipayConfig;

    @Override
    public PayOrderRespDTO unifiedOrder(PayOrderUnifiedReqDTO reqDTO) throws Throwable {
        // 1.1 构建 AlipayTradePagePayModel 请求
        AlipayTradePagePayModel model = new AlipayTradePagePayModel();
        // ① 通用的参数
        String outTradeNo = reqDTO.getOutTradeNo();
        model.setOutTradeNo(outTradeNo);
        model.setSubject(reqDTO.getSubject());
        model.setBody(reqDTO.getSubject());
        model.setTotalAmount(String.valueOf(reqDTO.getPrice()));
        model.setTimeExpire(LocalDateTimeUtil.format(reqDTO.getExpireTime(), NORM_DATETIME_FORMATTER));
        model.setProductCode(alipayConfig.getProductCode());
        model.setQrPayMode(alipayConfig.getQrPayMode());
        model.setPassbackParams(JSON.toJSONString(reqDTO.getPassbackParams()));

        // 1.2 构建 AlipayTradePagePayRequest 请求
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setBizModel(model);
        request.setNotifyUrl(reqDTO.getNotifyUrl());

        // 2.1 执行请求
        DefaultAlipayClient client = new DefaultAlipayClient(alipayConfig);
        AlipayTradePagePayResponse response = client.pageExecute(request, Method.GET.name());

        // 2.2 处理结果
        if (!response.isSuccess()) {
            Assert.isFalse(response.isSuccess());
            return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
                    outTradeNo, response);
        }
        return PayOrderRespDTO.waitingOf(null, response.getBody(),
                outTradeNo, response, PayChannelCodeEnum.ALIPAY_PC.getCode());
    }

    @Override
    public PayOrderRespDTO queryPayOrder(String outTradeNo) throws Throwable {
        // 1.1 构建 AlipayTradeRefundModel 请求
        AlipayTradeQueryModel model = new AlipayTradeQueryModel();
        model.setOutTradeNo(outTradeNo);
        // 1.2 构建 AlipayTradeQueryRequest 请求
        AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
        request.setBizModel(model);

        DefaultAlipayClient client = new DefaultAlipayClient(alipayConfig);
        AlipayTradeQueryResponse response = client.execute(request);
        if (!response.isSuccess()) { // 不成功，例如说订单不存在
            return PayOrderRespDTO.closedOf(response.getSubCode(), response.getSubMsg(),
                    outTradeNo, response);
        }
        // 2.2 解析订单的状态
        Integer status = parseStatus(response.getTradeStatus());
        Assert.notNull(status, () -> {
            throw new IllegalArgumentException(CharSequenceUtil.format("body({}) 的 trade_status 不正确", response.getBody()));
        });
        return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()),
                outTradeNo, response);
    }

    @Override
    public PayOrderRespDTO doParseOrderNotify(Map<String, String> params, String body, Map<String, String> headers) throws Throwable {
        // 1. 校验回调数据
        verifyNotifyData(params);

        // 2. 解析订单的状态
        // 额外说明：支付宝不仅仅支付成功会回调，再各种触发支付单数据变化时，都会进行回调，所以这里 status 的解析会写的比较复杂
        Map<String, String> bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8);
        Integer status = parseStatus(bodyObj.get("trade_status"));

        // 3.解析创建支付时候的个性化参数
        String passbackParamsStr = bodyObj.get(PayConstants.PAY_PASSBACK_PARAMS_KEY);
        Map<String, Object> passbackParams = JSON.parseObject(passbackParamsStr, Map.class);

        Assert.notNull(status, (Supplier<Throwable>) () -> {
            throw new IllegalArgumentException(CharSequenceUtil.format("body({}) 的 trade_status 不正确", body));
        });
        return PayOrderRespDTO.of(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")),
                bodyObj.get("out_trade_no"), body, passbackParams);
    }

    /**
     * 校验回调数据
     *
     * @param params 回调参数
     * @throws Throwable 验签失败时抛出异常
     */
    protected void verifyNotifyData(Map<String, String> params) throws AlipayApiException {
        boolean verify = AlipaySignature.rsaCheckV1(params, alipayConfig.getAlipayPublicKey(),
                    StandardCharsets.UTF_8.name(), alipayConfig.getSignType());
        Assert.isTrue(verify, "验签结果不通过");
    }

    /**
     * 解析查询状态
     *
     * @param tradeStatus
     * @return
     */
    private static Integer parseStatus(String tradeStatus) {
        return Objects.equals("WAIT_BUYER_PAY", tradeStatus) ? PayOrderStatusRespEnum.WAITING.getStatus()
                : ObjectUtils.equalsAny(tradeStatus, "TRADE_FINISHED", "TRADE_SUCCESS") ? PayOrderStatusRespEnum.SUCCESS.getStatus()
                : Objects.equals("TRADE_CLOSED", tradeStatus) ? PayOrderStatusRespEnum.CLOSED.getStatus() : null;
    }

    protected LocalDateTime parseTime(String str) {
        return LocalDateTimeUtil.parse(str, NORM_DATETIME_FORMATTER);
    }

}
