package com.yc.ship.module.trade.service.ota.impl;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;

import com.yc.ship.framework.common.exception.ServerException;
import com.yc.ship.framework.common.pojo.CommonResult;
import com.yc.ship.framework.common.util.collection.CollectionUtils;
import com.yc.ship.framework.tenant.core.context.TenantContextHolder;
import com.yc.ship.module.infra.api.config.ConfigApi;
import com.yc.ship.module.ota.api.OtaDistributorApi;
import com.yc.ship.module.ota.api.dto.DistributorProductRespDTO;
import com.yc.ship.module.ota.api.dto.DistributorRespDTO;
import com.yc.ship.module.product.api.ProductApi;
import com.yc.ship.module.product.api.dto.ProductSaleRuleRespDTO;
import com.yc.ship.module.product.api.dto.ProductSpuRespDTO;
import com.yc.ship.module.resource.service.resource.util.MD5Utils;
import com.yc.ship.module.trade.api.dto.TradeOrderCreateReqDTO;
import com.yc.ship.module.trade.api.dto.TradeOrderRespDTO;
import com.yc.ship.module.trade.api.dto.TradeRefundCreateReqDTO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.PayOrderReqVO;
import com.yc.ship.module.trade.controller.admin.order.vo.order.PayOrderRespVO;
import com.yc.ship.module.trade.convert.order.TradeRefundConvert;
import com.yc.ship.module.trade.convert.ota.OtaConvert;
import com.yc.ship.module.trade.dal.dataobject.order.TradeDetailDO;
import com.yc.ship.module.trade.dal.dataobject.order.TradeOrderDO;
import com.yc.ship.module.trade.enums.PayAccountTypeEnum;
import com.yc.ship.module.trade.enums.PayTypeEnum;
import com.yc.ship.module.trade.enums.TradeOrderStatusEnum;
import com.yc.ship.module.trade.enums.VoucherStatusEnum;
import com.yc.ship.module.trade.enums.ota.OtaConstants;
import com.yc.ship.module.trade.framework.common.ota.Sha256Util;
import com.yc.ship.module.trade.service.notifylog.NotifyLogService;
import com.yc.ship.module.trade.service.order.TradeOrderRepositoryService;
import com.yc.ship.module.trade.service.order.TradeOrderRuleValidate;
import com.yc.ship.module.trade.service.order.TradeOrderService;
import com.yc.ship.module.trade.service.ota.OtaService;
import com.yc.ship.module.trade.service.ota.TiktokLifeService;
import com.yc.ship.module.trade.service.ota.bo.SignBackBean;
import com.yc.ship.module.trade.service.pay.TradeOrderPayService;
import com.yc.ship.module.trade.service.refund.TradeRefundRepositoryService;
import com.yc.ship.module.trade.service.refund.TradeRefundService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.yc.ship.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.OTA_REQUEST_FAIL;
import static com.yc.ship.module.trade.enums.ErrorCodeConstants.OTA_REQUEST_TIMEOUT;
import static com.yc.ship.module.trade.enums.TravelhubDictTypeConstants.PRODUCT_BASE_TYPE_SELF;
import static com.yc.ship.module.trade.service.ota.OtaService.tiktokLife;


/**
 * @description：抖音期票
 */
@Service(tiktokLife)
@Slf4j
public class TiktokLifeServiceImpl implements TiktokLifeService, OtaService {

    @Resource
    private OtaDistributorApi otaDistributorApi;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private ProductApi productApi;
    @Resource
    private TradeOrderService tradeOrderService;
    @Resource
    private TradeOrderRepositoryService tradeOrderRepositoryService;
    @Resource
    private ConfigApi configApi;
    @Resource
    private TradeOrderPayService tradeOrderPayService;
    @Resource
    private NotifyLogService notifyLogService;
    @Resource
    private TiktokLifeService tiktokLifeService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private TradeRefundService tradeRefundService;

    @Resource
    private TradeRefundRepositoryService tradeRefundRepositoryService;


    /**
     * @param tenantId   租户
     * @param path       路径
     * @param request    HttpServletRequest
     * @param jsonStr    请求参数
     * @param versionKey 版本标识
     * @return java.lang.String
     * @description 抖音三方码-请求
     */
    @Override
    public String doRequest(Long tenantId, String path, HttpServletRequest request, String jsonStr, String versionKey) {
        try {
            TenantContextHolder.setTenantId(tenantId);
            TenantContextHolder.setIgnore(false);
            SignBackBean signBackBean = new SignBackBean();
            if (StringUtils.equals(versionKey, "life")) {
                signBackBean = this.verifyTiktokLife(path, request, jsonStr, versionKey);
            } else {
                signBackBean = this.verifyTiktok(path, request, jsonStr, versionKey);
            }
            DistributorRespDTO distributor = signBackBean.getDistributor();
            if (distributor == null) {
                distributor = new DistributorRespDTO();
            }
            // 验签结果
            if (!signBackBean.isSignSuccess()) {
                return signBackBean.getErrMsg();
            }
            String key = "[ota-in]" + distributor.getId();
            RLock lock = redissonClient.getLock(key);
            try {
                if (lock.tryLock(30, TimeUnit.SECONDS)) {
                    try {
                        return doRequestByPath(path, request, jsonStr, distributor, versionKey);
                    } catch (Exception ex) {
                        throw ex;
                    } finally {
                        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
                            lock.unlock();
                        }
                    }
                } else {
                    throw exception(OTA_REQUEST_TIMEOUT);
                }
            } catch (Exception ex) {
                throw exception(OTA_REQUEST_FAIL);
            }
        } catch (ServerException e) {
            return returnVerifyTiktok(2, 10, versionKey);
        } catch (Exception e) {
            // 其他异常
            return returnVerifyTiktok(2, 10, versionKey);
        }
    }

    public SignBackBean verifyTiktok(String path, HttpServletRequest request, String jsonStr, String versionKey) {
        SignBackBean bean = new SignBackBean();
        bean.setSignSuccess(false);
        // 获取分销商信息
        String clientKey = request.getParameter("client_key");
        long timestamp = Long.valueOf(request.getParameter("timestamp"));
        String sign = request.getParameter("sign");
        DistributorRespDTO distributor = getServer(clientKey);
        // 账号不存在
        if (distributor == null) {
            bean.setErrMsg(returnVerifyTiktok(2, 8, versionKey));
            return bean;
        }
        bean.setDistributor(distributor);
        String signStr = distributor.getSecret() + "&" + "client_key=" + distributor.getAppid() + "&timestamp=" + timestamp + "&http_body=" + jsonStr;
        // 签名结果(账号签名异常)
        if (!sign.equals(MD5Utils.MD5(signStr))) {
            log.error("分销{},{},{},{}", "抖音", path, sign + "==" + MD5Utils.MD5(signStr), "验签失败");
            bean.setErrMsg(returnVerifyTiktok(2, 9, versionKey));
            return bean;
        }
        bean.setSignSuccess(true);
        return bean;
    }

    public SignBackBean verifyTiktokLife(String path, HttpServletRequest request, String jsonStr, String versionKey) {
        SignBackBean bean = new SignBackBean();
        bean.setSignSuccess(false);
        // 获取分销商信息
        long timestamp = Long.valueOf(request.getParameter("timestamp"));
        Map<String, String> headersInfoMap = getHeadersInfo(request);
        String xLifeClientkey = headersInfoMap.get("x-life-clientkey");
        String xLifeSign = headersInfoMap.get("x-life-sign");
        DistributorRespDTO distributor = this.getServerTiktokLife(xLifeClientkey);
        // 账号不存在
        if (distributor == null) {
            bean.setErrMsg(returnVerifyTiktok(2, 8, versionKey));
            return bean;
        }
        bean.setDistributor(distributor);
        String signStr = distributor.getSecret() + "&" + "client_key=" + distributor.getAppid() + "&timestamp=" + timestamp + "&http_body=" + jsonStr;
        // 签名结果(账号签名异常)
        if (!xLifeSign.equals(Sha256Util.getSHA256(signStr))) {
            log.error("分销{},{},{},{}", "抖音", path, xLifeSign + "==" + Sha256Util.getSHA256(signStr), "验签失败");
            bean.setErrMsg(returnVerifyTiktok(2, 9, versionKey));
            return bean;
        }
        bean.setSignSuccess(true);
        return bean;
    }

    public String doRequestByPath(String path, HttpServletRequest request, String jsonStr, DistributorRespDTO distributor, String versionKey) {
        String method = path;
        // 内容体jsonStr
        JSONObject body = JSONUtil.parseObj(jsonStr);
        // 下单前校验
        if ("VerifyOrder".equals(method)) {
            return verifyOrder(body, distributor, versionKey);
            // 创建订单
        } else if ("CreateOrder".equals(method)) {
            return createOrder(body, distributor, versionKey);
            // 取消订单
        } else if ("CancelOrder".equals(method)) {
            return cancelOrder(body, distributor, versionKey, request);
            // 同步退款状态信息变更
        } else if ("RefundedOrder".equals(method)) {
            return refundedOrder(body, distributor, versionKey);
        } else {
            return returnVerifyTiktok(2, 10, versionKey);
        }
    }

    /**
     * @param request HttpServletRequest
     * @return java.util.Map<java.lang.String, java.lang.String>
     * @description 获取请求头参数
     * @author 杨涛
     * @date 2025/12/12 14:00
     */
    public Map<String, String> getHeadersInfo(HttpServletRequest request) {
        Map<String, String> map = new HashMap<String, String>();
        Enumeration headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = (String) headerNames.nextElement();
            String value = request.getHeader(key);
            map.put(key, value);
        }
        return map;
    }

    /**
     * @param body        参数
     * @param distributor 分销商信息
     * @param versionKey  版本标识
     * @return java.lang.String
     * @description 下单前校验
     * @author 杨涛
     * @date 2025/4/15 10:58
     */
    String verifyOrder(JSONObject body, DistributorRespDTO distributor, String versionKey) {
        // 查询产品

        Long distributorProductId = body.getLong("third_sku_id");
        DistributorProductRespDTO distributorProduct = otaDistributorApi.getDistributorProductById(distributorProductId).getCheckedData();

//        DistributorProductRespDTO distributorProduct = otaDistributorApi.getDistributorProductByProductAndDistributor(productId, distributor.getId()).getCheckedData();
        // 产品不存在
        if (null == distributorProduct) {
            return returnVerifyTiktok(2, 1, versionKey);
        }
        // 产品已下架
        if (distributorProduct.getIsUse() != 1) {
            return returnVerifyTiktok(2, 2, versionKey);
        }

        int tickets = Integer.parseInt(body.getStr("count"));

        TradeOrderCreateReqDTO order = this.parseOrder(body, distributor);
//        //验证分销产品库存
//        StockDistributorDeductReqDTO deductReqDTO = new StockDistributorDeductReqDTO();
//        deductReqDTO.setDistributorProductId(distributorProductId);
//        deductReqDTO.setNum(tickets);
//        deductReqDTO.setUseDate(order.getTravelDate());
//        try {
//            stockApi.deductDistributorStock(deductReqDTO).getCheckedData();
//        } catch (Exception e) {
//            log.error("抖音期票扣减库存异常：{}",  e.getMessage());
//            return returnVerifyTiktok(2, 4, versionKey);
//        }
        //验证销售规则
        ProductSpuRespDTO productSpuRespDTO = productApi.getProduct(distributorProduct.getProductId()).getCheckedData();
        TradeOrderRuleValidate.checkSaleRule(order.getItemlList().get(0), productSpuRespDTO);

        // 可以下单
        return returnVerifyTiktok(1, 0, versionKey);
    }

    /**
     * 比较两个日期大小 前者大于返回1 反之为0
     *
     * @param date1
     * @param date2
     * @return
     */
    public static boolean compareDate(Date date1, Date date2) {
        try {
            return date1.getTime() >= date2.getTime();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    /**
     * @param result     确认结果
     * @param failCode   不可下单原因
     * @param versionKey 版本标识
     * @return java.lang.String
     * @description 下单返回抖音信息
     * @author 杨涛
     * @date 2025/4/15 14:30
     */
    public String returnVerifyTiktok(int result, int failCode, String versionKey) {
        JSONObject returnObject = new JSONObject();
        returnCommon(versionKey, returnObject);

        JSONObject dataObject = new JSONObject();
        // 抖音来客版本
        if (StringUtils.equals(versionKey, "life")) {
            // 接口请求成功时务必确保返回的 error_code=0
            dataObject.put("error_code", 0);
            // 接口错误描述
            dataObject.put("description", "success");
        }
        // 确认结果1可以下单，2不可下单
        dataObject.put("result", result);
        // 不可下单原因
        if (result != 1) {
            dataObject.set("fail_code", failCode);
        }
        returnObject.set("data", dataObject);
        return returnObject.toString();
    }

    /**
     * @param body        参数
     * @param distributor 分销商信息
     * @param versionKey  版本标识
     * @return java.lang.String
     * @description 创建订单
     * @author 杨涛
     * @date 2025/4/15 11:00
     */
    String createOrder(JSONObject body, DistributorRespDTO distributor, String versionKey) {
        RLock lock = null;
        try {
            // 封装下单
            TradeOrderCreateReqDTO order = this.parseOrder(body, distributor);
            String key = order.getExternalOrderNo() + distributor.getId();
            lock = redissonClient.getLock(key);
            // 订单创建
            if (lock.tryLock(10, TimeUnit.SECONDS)) {
                return tiktokLifeService.savePayOrder(order.getExternalOrderNo(), order, true, distributor, versionKey);
            } else {
                return returnCreateTiktok(null, 2, versionKey);
            }
        } catch (ServerException ex) {
            log.error("分销{},{},{},{}", "抖音下单", "createOrder", body.getStr("orderSerialId"), ex.getMessage());
            return returnCreateTiktok(null, 2, versionKey);
        } catch (Exception e) {
            log.error("分销{},{},{},{}", "抖音下单", "createOrder", body.getStr("orderSerialId"), e.getMessage());
            return returnCreateTiktok(null, 2, versionKey);
        } finally {
            if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * @param body        参数
     * @param distributor 分销商信息
     * @return com.wisesoft.ticket.api.entity.order.OrderEntity
     * @description 封装下单
     */
    private TradeOrderCreateReqDTO parseOrder(JSONObject body, DistributorRespDTO distributor) throws ServerException {
        TradeOrderCreateReqDTO order = new TradeOrderCreateReqDTO();
        Long distributorId = distributor.getId();
        List<TradeOrderCreateReqDTO.OrderItem> itemlList = new ArrayList<>();
        List<TradeOrderCreateReqDTO.OrderDetail> detailList = new ArrayList<>();
        // 设置三方订单号(抖音侧订单号)
        order.setExternalOrderNo(body.getStr("order_id"));
        Long distributorProductId = body.getLong("third_sku_id");
        TradeOrderCreateReqDTO.OrderProperty orderProperty = OtaConvert.INSTANCE.convert(distributorId.toString(), distributor.getName());
        order.setOrderProperty(orderProperty);
        //2、查询产品
        DistributorProductRespDTO distributorProduct = otaDistributorApi.getDistributorProductById(distributorProductId).getCheckedData();
        ProductSpuRespDTO productSpuRespDTO = productApi.getProduct(distributorProduct.getProductId()).getCheckedData();
        order.setRoutePlanId(distributorProduct.getRoutePlanId());

        if (null == distributorProduct || distributorProduct.getIsUse() != 1) {
            throw new ServerException(1001, "产品已下架或不存在！");
        }

        // 判断产品是否是期票 0否，1是
        if (!Objects.equals(productSpuRespDTO.getProductBasicRule().getNeedPlayDate(), 0)) {
            throw new ServerException(1003, "该产品不是期票,产品编号:" + body.getStr("third_sku_id"));
        }
        // 设置订单门票数
        Integer ticketsCount = body.getInt("count");
        TradeOrderCreateReqDTO.OrderItem orderItem = new TradeOrderCreateReqDTO.OrderItem();
        orderItem.setProductId(productSpuRespDTO.getId().toString());
        orderItem.setProductType(PRODUCT_BASE_TYPE_SELF);
        List<DistributorProductRespDTO.DistributorProjectDTO> distributorProjectDTOS = otaDistributorApi.getProductByDistributor(distributor.getId(),distributorProduct.getProductId(),distributorProduct.getId()).getCheckedData();

        //联系人
        TradeOrderCreateReqDTO.OrderUser orderUser = new TradeOrderCreateReqDTO.OrderUser();
        // 设置证件类型(0：身份证，1护照，2人像，99：其他）
        orderUser.setCredentialType(0);
        if (body.containsKey("contact")) {
            JSONObject contactObject = JSONUtil.parseObj(body.getStr("contact"));
            // 设置联系人姓名
            orderUser.setContactName(contactObject.getStr("name"));
            // 设置联系人电话
            orderUser.setMobile(contactObject.getStr("phone"));
        } else {
            // 设置联系人姓名
            orderUser.setContactName("");
            // 设置联系人电话
            orderUser.setMobile("");
        }
        // 设置证件号
        orderUser.setCredentialNo("");
        // 设置联系人信息
        order.setOrderUser(orderUser);
        // 单位:元
        BigDecimal ticketPrice = distributorProduct.getSalePrice();
        if (ticketPrice != null) {
            BigDecimal orderAmt = ticketPrice.multiply(new BigDecimal(ticketsCount));
            order.setAmount(orderAmt);
            order.setPayAmount(orderAmt);
        }
        TradeOrderCreateReqDTO.OrderDetail orderDetail = OtaConvert.INSTANCE.convert(distributorProduct, ticketsCount,distributorProjectDTOS,ticketPrice);

        List<TradeOrderCreateReqDTO.Visitor> visitors = new ArrayList<>();
        // 获取出行人信息
        JSONArray vistors = JSONUtil.parseArray(body.getStr("tourists"));
        // 售票规则验证购票方式(1:实名购票，3：无需实名)

        ProductSaleRuleRespDTO saleRule = productSpuRespDTO.getProductSaleRule();
        // 产品是实名购票且出行人信息非空时
        if (saleRule != null && saleRule.getIsLimitType() == 1 && CollUtil.isNotEmpty(vistors)) {
            for (int i = 0; i < vistors.size(); i++) {
                JSONObject item = vistors.getJSONObject(i);
                String idCard = item.getStr("id_card");

                if (StringUtils.isEmpty(idCard)) {
                    throw new ServerException(1004, "游客身份证为空不能下单");
                }

                TradeOrderCreateReqDTO.Visitor visitor = new TradeOrderCreateReqDTO.Visitor();
                visitor.setCredentialNo(idCard);
                visitor.setCredentialType(0);
                visitor.setName(item.getStr("name"));
                visitors.add(visitor);
                if (i == 0) {
                    // 联系人，取第一个游客信息
                    TradeOrderCreateReqDTO.OrderUser orderUserNew = new TradeOrderCreateReqDTO.OrderUser();
                    // 设置联系人姓名
                    orderUserNew.setContactName(item.getStr("name"));
                    // 设置证件号类型
                    orderUserNew.setCredentialType(0);
                    // 设置证件号信息
                    orderUserNew.setCredentialNo(item.getStr("id_card"));
                    // 设置联系人电话
                    orderUserNew.setMobile(item.getStr("phone"));
                    order.setOrderUser(orderUserNew);
                }
            }
        } else {
            // 非实名制购票
            if (saleRule != null && saleRule.getIsLimitType() == 0) {
                if (CollUtil.isNotEmpty(vistors)) {
                    for (int i = 0; i < vistors.size(); i++) {
                        JSONObject item = vistors.getJSONObject(i);
                        if (i == 0) {
                            // 联系人，取第一个游客信息
                            TradeOrderCreateReqDTO.OrderUser orderUserNew = new TradeOrderCreateReqDTO.OrderUser();
                            // 设置联系人姓名
                            orderUserNew.setContactName(item.getStr("name"));
                            // 设置证件号类型
                            orderUserNew.setCredentialType(0);
                            // 设置证件号信息
                            orderUserNew.setCredentialNo(item.getStr("id_card"));
                            // 设置联系人电话
                            orderUserNew.setMobile(item.getStr("phone"));
                            order.setOrderUser(orderUserNew);
                        }
                    }
                }
                visitors = OtaConvert.INSTANCE.convertOtaVisitorList(ticketsCount);
                ;
            }
        }
        if (saleRule != null && saleRule.getIsLimitType() == 1 && orderDetail.getCount() != visitors.size()) {
            throw new ServerException(1005, "本商品为实名制商品，出行人必填！");
        }
        // 设置出行人信息
        orderDetail.setVisitorList(visitors);
        detailList.add(orderDetail);
        //设置订单明细
        orderItem.setDetailList(detailList);
        itemlList.add(orderItem);
        //设置订单项列表
        order.setItemlList(itemlList);
        return order;
    }


    /**
     * @param otaOrderNo             三方订单号
     * @param tradeOrderCreateReqDTO 订单信息
     * @param isPay                  是否下单即支付
     * @param distributor            分销商信息
     * @param versionKey             版本标识
     * @return java.lang.String
     * @description 带事务的创建订单
     */
    @Override
    public String savePayOrder(String otaOrderNo, TradeOrderCreateReqDTO tradeOrderCreateReqDTO, boolean isPay, DistributorRespDTO distributor, String versionKey) {
        // 重复验证

        TradeOrderDO orderDB = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(otaOrderNo, distributor.getId().toString());
        // 为空时
        if (orderDB == null) {
            // 创建订单
            CommonResult<TradeOrderRespDTO> result = tradeOrderService.createOrder(tradeOrderCreateReqDTO);
            if (result.isError()) {
                return returnCreateTiktok(null, 1, result.getMsg());
            }
            TradeOrderRespDTO tradeOrderRespDTO = result.getCheckedData();

            // 统一支付、扣款、修改订单状态
            if (isPay) {
                PayOrderReqVO payOrderReqVO = new PayOrderReqVO();
                payOrderReqVO.setAccountId(distributor.getId().toString());
                //ota
                payOrderReqVO.setAccountType(PayAccountTypeEnum.OTA.getType());

                payOrderReqVO.setOrderId(tradeOrderRespDTO.getOrderId());
                payOrderReqVO.setPayAmount(tradeOrderCreateReqDTO.getPayAmount());
                //预付款>授信
                Object[] payTypes = distributor.getPayType().split(",");
                if (CollectionUtils.containsAny("1", payTypes)) {
                    payOrderReqVO.setPayType(PayTypeEnum.ADVANCE_PAYMENT.getType());
                } else if (CollectionUtils.containsAny("2", payTypes)) {
                    payOrderReqVO.setPayType(PayTypeEnum.CREDIT_GRANTING.getType());
                }
                //统一支付、扣款、修改订单状态
                CommonResult<PayOrderRespVO> payResult = tradeOrderPayService.payOrder(payOrderReqVO);
                if (payResult.isError()) {
                    return returnCreateTiktok(tradeOrderRespDTO.getOrderId(), 2, payResult.getMsg());
                }
            }
            // 返回订单创建成功
            return returnCreateTiktok(tradeOrderRespDTO.getOrderId(), 1, versionKey);
            // 订单未支付
        } else if (orderDB.getOrderStatus() == 0) {
            return returnCreateTiktok(orderDB.getId(), 2, versionKey);
            // 订单已过期
        } else if (orderDB.getOrderStatus() == 6) {
            return returnCreateTiktok(orderDB.getId(), 2, versionKey);
            // 订单已存在
        } else {
            return returnCreateTiktok(orderDB.getId(), 1, versionKey);
        }

    }

    /**
     * @param orderId    订单信息
     * @param result     出票结果
     * @param versionKey 版本标识
     * @return java.lang.String
     * @description 下单返回抖音信息
     * @author 杨涛
     * @date 2025/4/15 14:30
     */
    public String returnCreateTiktok(Long orderId, int result, String versionKey) {
        JSONObject returnObject = new JSONObject();
        returnCommon(versionKey, returnObject);

        JSONObject dataObject = new JSONObject();
        if (StringUtils.equals(versionKey, "life")) {
            // 接口请求成功时务必确保返回的 error_code=0
            dataObject.put("error_code", 0);
            // 接口错误描述
            dataObject.put("description", "success");
        }
        // 发码结果:0发码中1成功2失败
        dataObject.put("result", result);
        // 下单成功时
        if (result == 1) {
            // 获取订单票号信息
            List<TradeDetailDO> detailList = tradeOrderRepositoryService.queryDetailByOrderId(orderId);
            // 有数据时
            if (CollUtil.isNotEmpty(detailList)) {
                JSONArray codes = new JSONArray();
                detailList.forEach(detail -> {
                    codes.add(detail.getVoucherCode());
                });
                // 三方码列表
                dataObject.put("codes", codes);
            }
        }
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    /**
     * @param body        参数
     * @param distributor 分销商信息
     * @param versionKey  版本标识
     * @param request     HttpServletRequest
     * @return java.lang.String
     * @description 订单退票
     * @author 杨涛
     * @date 2025/4/15 11:15
     */
    String cancelOrder(JSONObject body, DistributorRespDTO distributor, String versionKey, HttpServletRequest request) {
        // 票号集合
        List<String> ticketNoList = new ArrayList<>();
        RLock lock = null;
        try {
            Integer count = -1;
            // 要退票的抖音订单号
            String orderId = body.getStr("order_id");
            // 要退票的票号
            JSONArray certificates = JSONUtil.parseArray(body.getStr("certificates"));
            // 要退票的票号是否为空
            // 订单信息
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(orderId, distributor.getId().toString());
            // 订单不存在
            if (order == null) {
                return returnCancelTiktok(2, "订单不存在", ticketNoList, versionKey);
            }
            Integer isForceRefund = 0;
            TradeRefundCreateReqDTO refundCreateReqDTO = TradeRefundConvert.INSTANCE.convertOtaRefund(order.getId(), distributor.getId(), isForceRefund, order.getOrderNo());
            refundCreateReqDTO.setRefundAmount(order.getPayAmount());
            List<TradeDetailDO> detailDOList = tradeOrderRepositoryService.queryDetailByOrderId(order.getId());
            List<TradeRefundCreateReqDTO.RefundItem> refundItemList = new ArrayList<>();
            BigDecimal refundAmt = BigDecimal.ZERO;
            boolean isCertificatesBlankFlag = false;
            if (CollUtil.isNotEmpty(certificates)) {
                for (int i = 0; i < certificates.size(); i++) {
                    JSONObject certificate = certificates.getJSONObject(i);
                    String code = certificate.getStr("code");
                    if (StringUtils.isNotBlank(code)) {
                        TradeDetailDO tradeDetail = tradeOrderRepositoryService.queryDetailVoucherCode(code, order.getId());
                        TradeRefundCreateReqDTO.RefundItem refundItem = TradeRefundConvert.INSTANCE.convertRefundItem(tradeDetail.getActualPrice(), order.getId(), tradeDetail.getId(), tradeDetail.getVoucherCode());
                        refundAmt = refundAmt.add(tradeDetail.getActualPrice());
                        refundItemList.add(refundItem);
                    } else {
                        isCertificatesBlankFlag = true;
                    }
                }
                count = refundItemList.size();
                refundCreateReqDTO.setRefundAmount(refundAmt);
                refundCreateReqDTO.setItemlList(refundItemList);
            }

            // 要退票的票号是否为空且订单无效时 同意退款
            if (isCertificatesBlankFlag && (order == null || ObjectUtil.equals(order.getOrderStatus(), TradeOrderStatusEnum.INIT.getStatus())) || ObjectUtil.equals(order.getOrderStatus(), TradeOrderStatusEnum.FAIL.getStatus())) {
                return returnCancelTiktok(1, "", ticketNoList, versionKey);
            }

            // 该订单无可退门票
            Long effectyCount = tradeOrderRepositoryService.queryEffectyCount(order.getId());
            if (effectyCount == 0) {
                return returnCancelTiktok(2, "该订单无可退门票", ticketNoList, versionKey);
            }

            // 锁
            lock = redissonClient.getLock(order.getOrderNo() + distributor.getId());
            if (lock.tryLock(10, 30, TimeUnit.SECONDS)) {
                // 退票数缺省时退票所有可退门票
                if (count == -1) {
                    Map<String, Object> ticketMap = tradeOrderRepositoryService.queryDetailCountByOrderId(order.getId());
                    int checkCount = MapUtil.getInt(ticketMap, "checkCount");
                    int partCount = MapUtil.getInt(ticketMap, "partCount");
                    int outTimeCount = MapUtil.getInt(ticketMap, "outTimeCount");
                    int refundCount = MapUtil.getInt(ticketMap, "refundCount");
                    // 已使用票数
                    int useCount = checkCount + partCount + outTimeCount;
                    // 可退票数 = 总票数 - 已使用票数 - 已退票数
                    count = detailDOList.size() - useCount - refundCount;
                }
                tradeRefundService.refund(refundCreateReqDTO);

                return returnCancelTiktok(1, "", ticketNoList, versionKey);
            } else {
                return returnCancelTiktok(2, "重复提交", ticketNoList, versionKey);
            }
        } catch (ServerException ex) {
            return returnCancelTiktok(2, ex.getMessage(), ticketNoList, versionKey);
        } catch (Exception ex) {
            return returnCancelTiktok(2, "退单出现异常", ticketNoList, versionKey);
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }
    }

    /**
     * @param result     退票结果
     * @param reason     拒绝原因
     * @param codes      退票票号
     * @param versionKey 版本标识
     * @return java.lang.String
     * @description 已退款消息(退单 、 退票)
     * @author 杨涛
     * @date 2025/4/15 14:30
     */
    public String returnCancelTiktok(int result, String reason, List<String> codes, String versionKey) {
        JSONObject returnObject = new JSONObject();
        returnCommon(versionKey, returnObject);

        JSONObject dataObject = new JSONObject();
        // 抖音来客版本
        if (StringUtils.equals(versionKey, "life")) {
            // 接口请求成功时务必确保返回的 error_code=0
            dataObject.put("error_code", 0);
            // 接口错误描述
            dataObject.put("description", "success");
        }
        // 退票结果:0待确认1允许2拒绝
        dataObject.put("result", result);
        // 拒绝原因
        dataObject.put("reason", reason);
        // 退票票号
        dataObject.put("codes", codes);
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }

    /**
     * @param versionKey   版本标识
     * @param returnObject 返回信息
     * @return com.alibaba.fastjson.JSONObject
     * @description 通用返回信息封装
     * @author 杨涛
     * @date 2025/12/15 15:51
     */
    private JSONObject returnCommon(String versionKey, JSONObject returnObject) {
        if (StringUtils.equals(versionKey, "")) {
            JSONObject baseObject = new JSONObject();
            // 接口错误码
            baseObject.put("biz_code", 0);
            // 接口错误描述
            baseObject.put("biz_msg", "");
            // baseObject.put("logid", "2020070614111601022506808001045D59");
            returnObject.put("base", baseObject);
        }
        return returnObject;
    }

    /**
     * @param body        参数
     * @param distributor 分销商信息
     * @param versionKey  版本标识
     * @return java.lang.String
     * @description 已退款消息(退单 、 退票)
     * @author 杨涛
     * @date 2025/12/19 14:20
     */
    String refundedOrder(JSONObject body, DistributorRespDTO distributor, String versionKey) {
        List<TradeRefundCreateReqDTO.RefundItem> refundItemList = new ArrayList<>();
        BigDecimal refundAmt = BigDecimal.ZERO;
        RLock lock = null;
        try {
            // 退票列表
            Integer count = -1;
            // 要退票的抖音订单号
            String orderId = body.getStr("order_id");
            TradeOrderDO order = tradeOrderRepositoryService.getOrderByOtaAndExternalOrderNo(orderId, distributor.getId().toString());
            // 订单不存在
            if (order == null) {
                return returnRefundedOrder(1, "订单不存在", versionKey);
            }
            Integer isForceRefund = 1;

            TradeRefundCreateReqDTO refundCreateReqDTO = TradeRefundConvert.INSTANCE.convertOtaRefund(order.getId(), distributor.getId(), isForceRefund, order.getOrderNo());
            refundCreateReqDTO.setRefundAmount(order.getPayAmount());
            // 执行退单
            String key = order.getExternalOrderNo() + "#" + order.getSourceId();
            lock = redissonClient.getLock(key);
            if (lock.tryLock(20, 60, TimeUnit.SECONDS)) {
                // 要退票的票号
                JSONArray certificates = JSONUtil.parseArray(body.getStr("notice_list"));
                if (CollUtil.isNotEmpty(certificates)) {
                    for (int i = 0; i < certificates.size(); i++) {
                        JSONObject certificate = certificates.getJSONObject(i);
                        String code = certificate.getStr("code");
                        // 审核结果 1同意2拒绝
                        String auditResult = certificate.getStr("audit_result");
                        if (StringUtils.equals(auditResult, "1")) {
                            TradeDetailDO tradeDetail = tradeOrderRepositoryService.queryDetailVoucherCode(code, order.getId());
                            if (ObjectUtil.equals(tradeDetail.getVoucherStatus(), VoucherStatusEnum.REFUND.getStatus())) {
                                return returnRefundedOrder(1, "门票已退票", versionKey);
                            }
                            TradeRefundCreateReqDTO.RefundItem refundItem = TradeRefundConvert.INSTANCE.convertRefundItem(tradeDetail.getActualPrice(), order.getId(), tradeDetail.getId(), tradeDetail.getVoucherCode());
                            refundAmt = refundAmt.add(tradeDetail.getActualPrice());
                            refundItemList.add(refundItem);
                        }
                    }
                    refundCreateReqDTO.setRefundAmount(refundAmt);
                    refundCreateReqDTO.setItemlList(refundItemList);
                }
                // 订单已退票
                if (ObjectUtil.equals(order.getOrderStatus(), TradeOrderStatusEnum.ALL_REFUND.getStatus())) {
                    return returnRefundedOrder(1, "订单已退票", versionKey);
                }

                tradeRefundService.refund(refundCreateReqDTO);

                return returnRefundedOrder(0, "success", versionKey);
            } else {
                return returnRefundedOrder(1, "重复提交", versionKey);
            }
        } catch (ServerException ex) {
            log.error("抖音分销{},{},{},{}", "同步退款状态", "refundedOrder", refundItemList, ex.getMessage());
            return returnRefundedOrder(1, ex.getMessage(), versionKey);
        } catch (Exception ex) {
            log.error("抖音分销{},{},{},{}", "同步退款状态", "refundedOrder", refundItemList, ex.getMessage());
            return returnRefundedOrder(1, "同步退款状态出现异常", versionKey);
        } finally {
            if (null != lock && lock.isLocked() && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * @param errorCode  退票结果
     * @param result     拒绝原因
     * @param versionKey 版本标识
     * @return java.lang.String
     * @description 退票返回抖音信息
     * @author 杨涛
     * @date 2025/4/15 14:30
     */
    public String returnRefundedOrder(int errorCode, String result, String versionKey) {
        JSONObject returnObject = new JSONObject();
        JSONObject dataObject = new JSONObject();
        // 抖音来客版本
        if (StringUtils.equals(versionKey, "life")) {
            // 接口请求成功时务必确保返回的 error_code=0
            dataObject.put("error_code", errorCode);
            // 接口错误描述 success
            dataObject.put("description", result);
        }
        returnObject.put("data", dataObject);
        return returnObject.toString();
    }



    /**
     * @param distributor 分销商信息
     * @param map         参数
     * @return boolean
     * @description 核销通知抖音
     * @author 杨涛
     * @date 2025/4/15 11:20
     */
    @Override
    public boolean consumeNotice(DistributorRespDTO distributor, Map<String, Object> map) {
        String param = "";
        String sendUrl = "";
        HttpRequest httpRequest = null;
        try {
            JSONObject requestBody = new JSONObject();
            // 核销的抖音门店id
            requestBody.put("poi_id", distributor.getOtaIp());
            requestBody.put("verify_token", RandomStringUtils.randomAlphanumeric(32));
            List<String> ticketNoList = new ArrayList<>();
            ticketNoList.add(MapUtil.getStr(map, "voucherCode"));
            // 核销票码
            requestBody.put("codes", ticketNoList);
            // 抖音订单号
            requestBody.put("order_id", MapUtil.getStr(map, "external_order_no"));
            // serverurl:https://open.douyin.com/goodlife/v1/fulfilment/certificate/verify/
            sendUrl = distributor.getServerUrl() + "/goodlife/v1/fulfilment/certificate/verify/";

            param = requestBody.toString();

            Map<String, String> headParam = new HashMap<String, String>();
            // 一次验券的标识(客户端token)
            String clientToken = getClientToken(distributor);
            headParam.put("access-token", clientToken);
            // "?client_token=" + clientToken
            String sendResult = HttpUtil.createPost(sendUrl).contentType("application/json")
                    .body(param)
                    .headerMap(headParam, false)
                    .timeout(15000)
                    .execute().body();
//            String sendResult = SendHttps.sendPost(sendUrl, param, headParam);
            boolean verifyResultsFlag = getVerifyResults(sendResult, ticketNoList, sendUrl, param, map);
            return verifyResultsFlag;
        } catch (Exception ex) {
            log.error("抖音期票通知失败："+ex);
            // 记录消费通知
            notifyLogService.saveNotifyLog(map, sendUrl, param, ex.getMessage(), 0);
            return false;
        }
    }

//    public String getShopPoiId(DistributorRespDTO distributor, String orderId){
//        // https://open.douyin.com/goodlife/v1/shop/poi/query/?page=1&size=20&account_id=12345
//        String sendUrl = distributor.getServerUrl() + "/goodlife/v1/shop/poi/query/";
//        String param = "page=1&size=20&third_id=" + orderId;
//        String result = SendHttps.sendGet(sendUrl, param, null);
//        return "";
//    }

    public boolean getVerifyResults(String sendResult, List<String> ticketNoList, String sendUrl, String param, Map<String, Object> map) {
        JSONObject returnObject = JSONUtil.parseObj(sendResult);
        JSONObject dataObject = JSONUtil.parseObj(returnObject.getStr("data"));
        // 是否包含验券结果信息
        boolean verifyResultsFlag = dataObject.containsKey("verify_results");
        if (verifyResultsFlag) {
            // 验券结果
            JSONArray verifyResults = JSONUtil.parseArray(dataObject.getStr("verify_results"));
            if (null != ticketNoList && !ticketNoList.isEmpty()) {
                JSONObject result = verifyResults.getJSONObject(0);
                // 记录消费通知 0 验券成功 1208 券码已核销
                boolean resultFlag = "0".equals(result.getStr("result")) || "1208".equals(result.getStr("result"));
                if (resultFlag) {
                    notifyLogService.saveNotifyLog(map, sendUrl, param, result.toString(), 1);
                    return true;
                    // 记录消费通知
                } else {
                    notifyLogService.saveNotifyLog(map, sendUrl, param, sendResult, 0);
                    return false;
                }
            }
        }
        // 未返回验券结果时,记录返回内容
        notifyLogService.saveNotifyLog(map, sendUrl, param, sendResult, 0);
        return false;
    }

    /**
     * @param distributor 分销商信息
     * @return java.lang.String
     * @description 获取客户端token
     * @author 杨涛
     * @date 2025/4/18 14:45
     */
    public String getClientToken(DistributorRespDTO distributor) {
        String clientToken = "";
        String clientTokenKey = "tiktok_access_token_" + distributor.getAppid();
        String accessToken = stringRedisTemplate.opsForValue().get(clientTokenKey);
        if (StringUtils.isNotBlank(accessToken)) {
            clientToken = accessToken;
        } else {
            RLock lock = null;
            try {
                lock = redissonClient.getLock("tiktok_access_token_lock");
                if (lock.tryLock(30, TimeUnit.SECONDS)) {
                    Map<String, String> header = new HashMap<>(16);
                    String clientTokenParam = "client_key=" + distributor.getAppid() + "&client_secret=" + distributor.getSecret() + "&grant_type=client_credential";
                    // serverurl:https://open.douyin.com
//                    String clientTokenResult = SendHttps.sendPostForm(distributor.getServerUrl() + "/oauth/client_token/", clientTokenParam, header);
                    String clientTokenResult = HttpUtil.createPost(distributor.getServerUrl() + "/oauth/client_token/").contentType("application/json")
                            .body(clientTokenParam)
                            .timeout(15000)
                            .execute().body();
                    if (StringUtils.isNotBlank(clientTokenResult)) {
                        JSONObject clientTokenObject = JSONUtil.parseObj(clientTokenResult);
                        JSONObject clientTokenDataObject = JSONUtil.parseObj(clientTokenObject.getStr("data"));
                        if (clientTokenDataObject.containsKey("access_token") &&
                                StringUtils.isNotBlank(clientTokenDataObject.getStr("access_token"))) {
                            clientToken = clientTokenDataObject.getStr("access_token");
                            // 118 分钟 7080秒
                            stringRedisTemplate.opsForValue().set(clientTokenKey, clientToken, 7080);
                        }
                    }
                }
            } catch (Exception ex) {
                log.error("分销{},{},{},{}", "生成 client_token", "/oauth/client_token/", "", ex.getMessage());
            } finally {
                if (lock != null && lock.isLocked() && lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        }
//        return "douyin.23UfpdWgJkgUyy1o1s6ytexso5TOFq5hViGnpxouScp3V38qeMYlmGy9Emg5";
        return clientToken;
    }

    /**
     * @param appId 账号
     * @return com.wisesoft.ticket.api.entity.ota.DistributorRespDTO
     * @description 获取抖音租户信息
     * @author 杨涛
     * @date 2025/4/15 10:55
     */
    DistributorRespDTO getServer(String appId) {
        // 获取抖音租户信息
        CommonResult<DistributorRespDTO> result = otaDistributorApi.getDistributorByOtaAccount(appId, OtaConstants.OTA_TIKTOK_TYPECODE);
        if (result.isError()) {
            return null;
        }
        return result.getCheckedData();
    }

    /**
     * @param appId 账号
     * @return com.wisesoft.ticket.api.entity.ota.DistributorRespDTO
     * @description 获取抖音租户信息
     */
    DistributorRespDTO getServerTiktokLife(String appId) {
        //获取分销商信息
        CommonResult<DistributorRespDTO> result = otaDistributorApi.getDistributorByOtaAccount(appId, OtaConstants.OTA_TIKTOK_LIFE_TYPECODE);
        if (result.isError()) {
            return null;
        }
        return result.getCheckedData();
    }


    /**
     * 暂未实现
     * @param distributor 分销商信息
     * @param map       参数
     * @return
     */
    @Override
    public boolean refundNotice(DistributorRespDTO distributor, Map<String, Object> map) {
        return false;
    }
}



