package com.seer.teach.pay.app.refund.service.impl;

import com.seer.teach.common.enums.pay.PayChannelEnum;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.enums.pay.PayOrderSourceEnum;
import com.seer.teach.common.enums.pay.RefundStatusEnum;
import com.seer.teach.common.enums.pay.RefundTypeEnum;
import com.seer.teach.common.exception.CommonException;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.common.utils.OrderIdGenerator;
import com.seer.teach.pay.api.refund.dto.RefundOrderRequestDTO;
import com.seer.teach.pay.app.client.PayClient;
import com.seer.teach.pay.app.client.convert.PayClientConvert;
import com.seer.teach.pay.app.client.dto.RefundNotificationRespDTO;
import com.seer.teach.pay.app.client.factory.PayClientFactory;
import com.seer.teach.pay.app.notify.service.OrderPayNotifyHandler;
import com.seer.teach.pay.app.notify.service.OrderProcessStrategyFactory;
import com.seer.teach.pay.app.client.dto.PayRefundReqDTO;
import com.seer.teach.pay.app.refund.service.IAppRefundOrderService;
import com.seer.teach.pay.app.refund.service.dto.RefundResultDTO;
import com.seer.teach.pay.dto.RefundSubmitRespDTO;
import com.seer.teach.pay.entity.PayOrderEntity;
import com.seer.teach.pay.entity.RefundOrderEntity;
import com.seer.teach.pay.service.IPayOrderService;
import com.seer.teach.pay.service.IRefundOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.Optional;

/**
 * 应用层退款服务实现类
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class AppRefundOrderServiceImpl implements IAppRefundOrderService {

    private final PayClientFactory payClientFactory;

    private final IPayOrderService payOrderService;

    private final IRefundOrderService refundOrderService;

    private final OrderProcessStrategyFactory orderProcessStrategyFactory;

    /**
     * 执行退款订单
     *
     * @param refundRequest 退款请求参数
     * @return 退款响应结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RefundSubmitRespDTO refundOrder(RefundOrderRequestDTO refundRequest) {
        log.info("开始处理退款订单，退款请求参数：{}", refundRequest);
        try {
            // 获取退款单号
            String merchantRefundSn = refundRequest.getMerchantRefundSn();
            String merchantOrderSn = refundRequest.getMerchantOrderSn();
            log.info("原支付订单号：{}", merchantOrderSn);

            PayOrderEntity payOrder = payOrderService.getOrderByMerchantOrderSn(merchantOrderSn);
            log.info("查询原支付订单结果，订单号：{}，查询结果：{}", merchantOrderSn, payOrder);

            AssertUtils.notNull(payOrder, ResultCodeEnum.PAY_ORDER_IS_NOT_EXIST);
            String channelCode = payOrder.getChannelCode();

            // 插入退款订单
            String refundSn = OrderIdGenerator.generateOrderId(OrderIdGenerator.PAY_REFUND_ORDER_PREFIX);
            log.info("生成的退款单号：{}", refundSn);
            RefundOrderEntity refundOrder = new RefundOrderEntity();
            refundOrder.setRefundSn(refundSn);
            refundOrder.setPayOrderSn(payOrder.getOrderSn());
            refundOrder.setUserId(refundRequest.getUserId());
            refundOrder.setChannelCode(channelCode);
            refundOrder.setRefundReason(refundRequest.getRefundReason());
            refundOrder.setRefundAmount(refundRequest.getRefundAmount());
            refundOrder.setTotalAmount(payOrder.getTotalAmount());
            refundOrder.setMerchantRefundSn(merchantRefundSn);
            refundOrder.setMerchantOrderSn(merchantOrderSn);
            refundOrder.setRefundType(refundRequest.getRefundType().getCode());
            refundOrder.setStatus(RefundStatusEnum.PENDING_WECHAT_REFUND.getCode());
            boolean saved = refundOrderService.save(refundOrder);
            log.info("插入退款记录表结果：{}，退款订单ID：{}", saved, refundOrder.getId());
            if (!saved) {
                log.error("退款订单保存失败，退款请求参数：{}", refundRequest);
                throw new CommonException(ResultCodeEnum.REFUND_ORDER_SAVE_FAILED);
            }
            // 获取支付渠道
            PayChannelEnum channel = PayChannelEnum.fromCode(channelCode);
            log.info("获取支付渠道结果：{}", channel);
            AssertUtils.notNull(channel, ResultCodeEnum.PAY_CHANNEL_NOT_SUPPORTED);
            PayClient payClient = payClientFactory.getPayClient(channel);
            log.info("获取支付客户端完成：{}", payClient != null);

            // 执行退款订单
            PayRefundReqDTO payRefundReqDTO = PayRefundReqDTO.builder()
                    .refundOrderId(refundOrder.getId().longValue())
                    .outTradeNo(payOrder.getOrderSn())
                    .outRefundNo(refundSn)
                    .reason(refundRequest.getRefundReason())
                    .payPrice(refundRequest.getTotalAmount())
                    .refundPrice(refundRequest.getRefundAmount())
                    .userId(refundRequest.getUserId())
                    .build();
            log.info("构建退款请求参数完成，请求参数：{}", payRefundReqDTO);

            RefundSubmitRespDTO refundSubmitResp = payClient.refundOrder(payRefundReqDTO);
            refundSubmitResp.setMerchantRefundSn(merchantRefundSn);
            log.info("调用支付客户端执行退款完成，响应结果：{}", refundSubmitResp);

            if (!payClient.isAsyncCallback()) {
                log.info("支付渠道为同步回调模式，开始处理退款结果");
                // 创建退款结果DTO
                RefundResultDTO refundResultDTO =  PayClientConvert.INSTANCE.convertRefundSubmitResp2Result(refundSubmitResp);

                handleRefundOrderResult(refundResultDTO);
            } else {
                log.info("支付渠道为异步回调模式，等待回调处理");
            }
            return refundSubmitResp;
        } catch (Exception e) {
            log.error("处理退款订单发生异常，退款请求参数：{}", refundRequest, e);
            throw e;
        }
    }

    /**
     * 查询退款状态
     *
     * @param refundSn 退款单号
     * @return 退款订单信息
     */
    @Override
    public RefundSubmitRespDTO queryRefundStatus(String refundSn) {
        log.info("查询退款状态，退款单号：{}", refundSn);
        try {
            // 根据退款单号查询支付订单，获取支付渠道编码
            PayClient payClient = getPayStrategy(refundSn);
            log.info("获取支付客户端完成，退款单号：{}", refundSn);

            RefundSubmitRespDTO result = payClient.doQueryRefundStatus(refundSn);
            log.info("查询退款状态完成，退款单号：{}，查询结果：{}", refundSn, result);
            return result;
        } catch (Exception e) {
            log.error("查询退款状态发生异常，退款单号：{}", refundSn, e);
            throw e;
        }
    }


    @Override
    public boolean refundCallback(String channelCode, Map<String, String> headers, Map<String, String> params, String body) {
        log.info("开始处理统一退款回调，渠道编码：{}", channelCode);
        try {
            // 根据渠道编码获取对应的支付策略
            PayChannelEnum channel = PayChannelEnum.fromCode(channelCode);
            log.info("获取的支付策略：{}", channel);
            AssertUtils.notNull(channel, ResultCodeEnum.PAY_CHANNEL_NOT_SUPPORTED);

            PayClient payClient = payClientFactory.getPayClient(channel);
            log.info("获取支付客户端完成，渠道编码：{}", channelCode);

            // 处理退款回调
            Optional<RefundNotificationRespDTO> refundNotificationRespDTO = payClient.handleRefundCallback(headers, params, body);
            log.info("处理退款回调完成，渠道编码：{}，处理结果是否存在：{}", channelCode, refundNotificationRespDTO.isPresent());

            if (!refundNotificationRespDTO.isPresent()) {
                log.warn("退款回调处理结果为空，渠道编码：{}", channelCode);
                return false;
            }

            RefundNotificationRespDTO refundNotificationResp = refundNotificationRespDTO.get();

            RefundOrderEntity refundOrder = refundOrderService.getRefundOrderByRefundSn(refundNotificationResp.getRefundNo());
            log.info("回调处理中查询退款订单，退款单号：{}，查询结果：{}", refundNotificationResp.getRefundNo(), refundOrder != null);

            AssertUtils.notNull(refundOrder, ResultCodeEnum.REFUND_ORDER_IS_NOT_EXIST);

            RefundResultDTO refundResultDTO = PayClientConvert.INSTANCE.convertRefundNotificationResp2Result(refundNotificationResp);
            log.info("获取的退款订单信息：{}", refundResultDTO);

            handleRefundOrderResult(refundResultDTO);

            boolean result = RefundStatusEnum.REFUND_SUCCESS.getCode() == refundOrder.getStatus();
            log.info("处理统一退款回调结果：{}，退款订单状态：{}，处理结果：{}", refundOrder, refundOrder.getStatus(), result);

            return result;
        } catch (Exception e) {
            log.error("处理统一退款回调失败，渠道编码：{}", channelCode, e);
            return false;
        }
    }

    private void handleRefundOrderResult(RefundResultDTO refundResultDTO) {
        log.info("开始处理退款订单结果，退款结果DTO：{}", refundResultDTO);
        try {
            // 根据退款订单SN查询退款订单
            RefundOrderEntity refundOrder = refundOrderService.getRefundOrderByRefundSn(refundResultDTO.getRefundOrderSn());
            log.info("查询退款订单结果，退款订单SN：{}，查询结果：{}", refundResultDTO.getRefundOrderSn(), refundOrder);
            AssertUtils.notNull(refundOrder, ResultCodeEnum.REFUND_ORDER_IS_NOT_EXIST);
            
            PayOrderEntity payOrder = payOrderService.getOrderByOrderSn(refundOrder.getPayOrderSn());
            log.info("原支付订单：{}，查询结果：{}", refundOrder.getPayOrderSn(), payOrder);
            AssertUtils.notNull(payOrder, ResultCodeEnum.PAY_ORDER_IS_NOT_EXIST);

            PayOrderSourceEnum payOrderSourceEnum = PayOrderSourceEnum.fromCode(payOrder.getOrderSource());
            log.info("支付订单来源：{}", payOrderSourceEnum);

            AssertUtils.notNull(payOrderSourceEnum, ResultCodeEnum.PAY_ORDER_SOURCE_NOT_SUPPORTED);

            OrderPayNotifyHandler handler = orderProcessStrategyFactory.getStrategy(payOrderSourceEnum);
            log.info("获取订单处理策略完成：{}", handler != null);

            RefundOrderEntity refundOrderEntity = PayClientConvert.INSTANCE.convert(refundResultDTO);
            refundOrderEntity.setId(refundOrder.getId());
            refundOrderEntity.setMerchantOrderSn(refundOrder.getMerchantOrderSn());
            refundOrderEntity.setMerchantRefundSn(refundOrder.getMerchantRefundSn());
            refundOrderEntity.setChannelCode(refundResultDTO.getChannelCode());
            boolean updated = refundOrderService.updateById(refundOrderEntity);
            log.info("更新退款订单结果：{}，退款订单ID：{}", updated, refundOrder.getId());

            // 获取处理后的退款订单
            RefundOrderEntity afterRefundOrder = refundOrderService.getById(refundOrder.getId());
            if (RefundStatusEnum.REFUND_SUCCESS.getCode() == refundOrderEntity.getStatus()) {
                log.info("退款成功，调用订单退款成功处理逻辑");
                if (handler != null) {
                    handler.orderRefundAfterSuccess(afterRefundOrder);
                } else {
                    log.warn("订单处理策略为空，无法调用退款成功处理逻辑，退款订单ID：{}", refundOrder.getId());
                }
            } else {
                log.info("退款失败，调用订单退款失败处理逻辑，退款状态：{}", refundOrder.getStatus());
                if (handler != null) {
                    handler.orderRefundAfterFail(afterRefundOrder);
                } else {
                    log.warn("订单处理策略为空，无法调用退款失败处理逻辑，退款订单ID：{}", refundOrder.getId());
                }
            }
            log.info("处理退款订单结果完成，退款订单ID：{}", refundOrder.getId());
        } catch (Exception e) {
            log.error("处理退款订单结果发生异常，退款结果DTO：{}", refundResultDTO, e);
            throw e;
        }
    }

    /**
     * 根据退款单号获取支付策略
     *
     * @param refundSn 退款单号
     * @return 支付策略
     */
    private PayClient getPayStrategy(String refundSn) {
        log.info("开始获取支付策略，退款单号：{}", refundSn);
        try {
            // 根据退款单号查询退款订单
            RefundOrderEntity refundOrder = refundOrderService.getRefundOrderByRefundSn(refundSn);
            log.info("根据退款单号查询退款订单完成，退款单号：{}，查询结果：{}", refundSn, refundOrder != null);

            AssertUtils.notNull(refundOrder, ResultCodeEnum.ORDER_NOT_FOUND);

            String channelCode = refundOrder.getChannelCode();
            log.info("退款单号：{}，支付渠道：{}", refundSn, channelCode);

            PayChannelEnum channel = PayChannelEnum.fromCode(channelCode);
            log.info("支付渠道枚举转换结果：{}", channel);
            PayClient result = payClientFactory.getPayClient(channel);
            log.info("获取支付策略完成，退款单号：{}，支付策略：{}", refundSn, result != null);
            return result;
        } catch (Exception e) {
            log.error("获取支付策略发生异常，退款单号：{}", refundSn, e);
            throw e;
        }
    }
}