package com.czg.impl;

import com.alibaba.fastjson2.JSONObject;
import com.czg.PayAdapter;
import com.czg.PayCst;
import com.czg.PolyPayUtils;
import com.czg.constant.PayChannelCst;
import com.czg.entity.resp.CzgBaseResp;
import com.czg.entity.resp.CzgRefundResp;
import com.czg.enums.CzgPayEnum;
import com.czg.exception.CzgException;
import com.czg.pay.*;
import com.czg.resp.CzgResult;
import com.czg.utils.AssertUtil;
import jakarta.validation.constraints.NotBlank;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;

import java.util.Map;

/**
 * @author ww
 */
@Slf4j
public class PolyPayAdapter implements PayAdapter {
    @Override
    public String getChannel() {
        return PayChannelCst.POLY;
    }

    @Override
    public CzgResult<Map<String, Object>> pay(@NonNull CzgPayEnum payType, @NotBlank String payData, @NotBlank String domain,
                                              @NotBlank String notifyUrl, CzgPayBaseReq bizData) {
        try {
            PolyMerchantDTO polyMerchantDTO = JSONObject.parseObject(payData, PolyMerchantDTO.class);
            return switch (payType) {
                case H5_PAY -> h5Pay(polyMerchantDTO, domain, notifyUrl, bizData);
                case JS_PAY -> jsPay(polyMerchantDTO, domain, notifyUrl, bizData);
                case LT_PAY -> ltPay(polyMerchantDTO, domain, notifyUrl, bizData);
                case SCAN_PAY -> scanPay(polyMerchantDTO, domain, notifyUrl, bizData);
                case MICRO_PAY -> microPay(polyMerchantDTO, domain, notifyUrl, bizData);
                default -> throw new CzgException("聚合支付不支持该支付方式: " + payType);
            };
        } catch (Exception e) {
            log.error("聚合支付处理失败: {}", e.getMessage(), e);
            return CzgResult.failure("聚合支付处理失败: " + e.getMessage());
        }
    }


    @Override
    public CzgResult<RefundRespDTO> refund(@NotBlank String domain, @NotBlank String payData, CzgRefundReq bizData) {
        PolyMerchantDTO shopMerchant = JSONObject.parseObject(payData, PolyMerchantDTO.class);
        CzgResult<CzgRefundResp> result = PolyPayUtils.refundOrder(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), bizData);
        return convertRefundResp(result);
    }

    @Override
    public CzgResult<QueryOrderRespDTO> queryPayOrder(@NotBlank String payData, @NotBlank String domain, String payOrderId, String mchOrderNo, String platform) {
        PolyMerchantDTO shopMerchant = JSONObject.parseObject(payData, PolyMerchantDTO.class);
        CzgResult<CzgBaseResp> result = PolyPayUtils.queryPayOrder(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), payOrderId, mchOrderNo);
        if (result.isSuccess()) {
            QueryOrderRespDTO respDTO = new QueryOrderRespDTO()
                    .setStatus(result.getData().getState())
                    .setOrderNo(result.getData().getMchOrderNo())
                    .setAmount(result.getData().getAmount())
                    .setErrorMsg("")
                    .setOriginResp(JSONObject.toJSONString(result.getData()));
            return CzgResult.success(respDTO);
        }
        return CzgResult.failure(result.getMsg());
    }

    @Override
    public CzgResult<RefundRespDTO> queryRefund(@NotBlank String payData, @NotBlank String domain, String mchRefundNo, String refundOrderId) {
        PolyMerchantDTO shopMerchant = JSONObject.parseObject(payData, PolyMerchantDTO.class);
        CzgResult<CzgRefundResp> result = PolyPayUtils.queryRefundOrder(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), mchRefundNo, refundOrderId);
        return convertRefundResp(result);
    }

    private CzgResult<Map<String, Object>> h5Pay(PolyMerchantDTO shopMerchant, String domain, String notifyUrl, CzgPayBaseReq bizData) {
        bizData.polyBase(shopMerchant.getStoreId(), notifyUrl);
        return PolyPayUtils.h5Pay(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), bizData);
    }

    private CzgResult<Map<String, Object>> jsPay(PolyMerchantDTO shopMerchant, String domain, String notifyUrl, CzgPayBaseReq bizData) {
        AssertUtil.isBlank(bizData.getSubAppid(), "暂不可用，请联系商家配置" + (PayCst.Type.ALIPAY.equals(bizData.getPayType()) ? "支付宝" : "微信") + "小程序");
        bizData.setPayType(PayCst.Type.ALIPAY.equals(bizData.getPayType()) ? "ALIPAY" : "WECHAT");
        bizData.polyBase(shopMerchant.getStoreId(), notifyUrl);
        return PolyPayUtils.jsPay(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), bizData);

    }

    private CzgResult<Map<String, Object>> ltPay(PolyMerchantDTO shopMerchant, String domain, String notifyUrl, CzgPayBaseReq bizData) {
        AssertUtil.isBlank(bizData.getSubAppid(), "暂不可用，请联系商家配置" + (PayCst.Type.ALIPAY.equals(bizData.getPayType()) ? "支付宝" : "微信") + "小程序");
        bizData.polyBase(shopMerchant.getStoreId(), notifyUrl);
        return PolyPayUtils.ltPay(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), bizData);
    }

    private CzgResult<Map<String, Object>> scanPay(PolyMerchantDTO shopMerchant, String domain, String notifyUrl, CzgPayBaseReq bizData) {
        bizData.polyBase(shopMerchant.getStoreId(), notifyUrl);
        return PolyPayUtils.scanPay(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), bizData);
    }

    private CzgResult<Map<String, Object>> microPay(PolyMerchantDTO shopMerchant, String domain, String notifyUrl, CzgPayBaseReq bizData) {
        bizData.polyBase(shopMerchant.getStoreId(), notifyUrl);
        return PolyPayUtils.microPay(domain, shopMerchant.getAppId(), shopMerchant.getAppSecret(), bizData);
    }

    private CzgResult<RefundRespDTO> convertRefundResp(CzgResult<CzgRefundResp> result) {
        if (result.isSuccess()) {
            RefundRespDTO respDTO = new RefundRespDTO()
                    .setStatus(result.getData().getState())
                    .setMerchantRefundNo(result.getData().getMchRefundNo())
                    .setThirdRefundNo(result.getData().getRefundOrderId())
                    .setPlatform(result.getData().getPayType())
                    .setRefundAmount(result.getData().getRefundAmt())
                    .setRefundTime(result.getData().getRefundTime())
                    .setOriginalData(result.getData().getExtParam());
            return CzgResult.success(respDTO);
        }

        return CzgResult.failure(result.getMsg());
    }
}
