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

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.seer.teach.common.enums.pay.PayChannelEnum;
import com.seer.teach.common.enums.pay.PayStatusEnum;
import com.seer.teach.common.enums.ResultCodeEnum;
import com.seer.teach.common.enums.pay.PayOrderSourceEnum;
import com.seer.teach.common.utils.AssertUtils;
import com.seer.teach.pay.app.client.convert.PayClientConvert;
import com.seer.teach.pay.app.client.dto.PayOrderReqDTO;
import com.seer.teach.pay.app.pay.controller.req.OrderPayReq;
import com.seer.teach.pay.app.client.dto.PayOrderRespDTO;
import com.seer.teach.pay.app.pay.controller.resp.AppPayOrderResp;
import com.seer.teach.pay.app.pay.convert.AppPayOrderConvert;
import com.seer.teach.pay.app.pay.service.IAppPayOrderService;
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.PayClient;
import com.seer.teach.pay.app.client.factory.PayClientFactory;
import com.seer.teach.pay.entity.PayChannelEntity;
import com.seer.teach.pay.entity.PayOrderEntity;
import com.seer.teach.pay.service.IPayChannelService;
import com.seer.teach.pay.service.IPayOrderService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Objects;

@Slf4j
@Service
@RequiredArgsConstructor
public class AppPayOrderServiceImpl implements IAppPayOrderService {

    private final PayClientFactory payClientFactory;

    private final IPayOrderService payOrderService;

    private final OrderProcessStrategyFactory orderProcessStrategyFactory;

    private final IPayChannelService payChannelService;

    @Override
    public AppPayOrderResp payOrder(OrderPayReq orderPayReq) {
        log.info("开始处理支付订单，渠道：{}，商户订单号：{}", orderPayReq.getChannelCode(), orderPayReq.getMerchantOrderSn());

        String channelCode = orderPayReq.getChannelCode();
        // 1.检查渠道
        PayChannelEntity payChannel = payChannelService.checkChannelByChannelCode(channelCode);

        // 2.查询支付订单
        PayOrderEntity payOrderEntity = payOrderService.getOrderByMerchantOrderSn(orderPayReq.getMerchantOrderSn());

        AssertUtils.notNull(payOrderEntity, ResultCodeEnum.PAY_ORDER_IS_NOT_EXIST);

        // 3.判断订单状态，只能待支付或则支付失败的订单才能进行支付
        AssertUtils.isTrue(payOrderEntity.getStatus() == PayStatusEnum.PENDING.getCode() || payOrderEntity.getStatus() == PayStatusEnum.PAY_FAILED.getCode(), ResultCodeEnum.PAY_ORDER_IS_PROCESSED);

        // 4.获取对应的支付客户端
        PayClient payCLient = payClientFactory.getPayClient(orderPayReq.getChannelCode());

        // 5，执行支付订单
        PayOrderReqDTO payOrderReqDTO = PayClientConvert.INSTANCE.convert2PayOrderReqDTO(payOrderEntity,orderPayReq);
        PayOrderRespDTO response = payCLient.payOrder(payOrderReqDTO);
        response.setChannelId(payChannel.getId());

        if (!payCLient.isAsyncCallback()) {
            handlePayResult(response);
        }
        log.info("支付订单处理完成，订单号：{}", orderPayReq.getMerchantOrderSn());
        return AppPayOrderConvert.INSTANCE.convertOne(response);
    }

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

            AssertUtils.notNull(channel, ResultCodeEnum.PAY_CHANNEL_IS_NOT_EXIST);

            PayClient payClient = payClientFactory.getPayClient(channel);
            // 处理支付回调
            PayOrderRespDTO response = payClient.handlePayCallback(headers, params, body);

            handlePayResult(response);
            return true;
        } catch (Exception e) {
            log.error("处理统一支付回调失败，渠道编码：{}", channelCode, e);
            return false;
        }
    }

    /**
     * 处理支付结果后的业务逻辑
     *
     * @param payOrderRespDTO 订单
     */
    private void handlePayResult(PayOrderRespDTO payOrderRespDTO) {
        // 更新支付订单状态
        PayOrderEntity payOrder = payOrderService.getOrderByOrderSn(payOrderRespDTO.getPayOrderSn());
        AssertUtils.notNull(payOrder, ResultCodeEnum.ORDER_NOT_FOUND);

        payOrder.setStatus(payOrderRespDTO.getStatus());
        payOrder.setTransactionId(payOrderRespDTO.getTransactionId());
        payOrder.setSuccessTime(payOrderRespDTO.getPaySuccessTime());
        payOrder.setChannelCode(payOrderRespDTO.getChannelCode());
        payOrder.setChannelId(payOrderRespDTO.getChannelId());
        boolean updateResult = payOrderService.updateById(payOrder);

        log.info("更新支付订单状态结果：{}", updateResult);
        OrderPayNotifyHandler handler = orderProcessStrategyFactory.getStrategy(PayOrderSourceEnum.fromCode(payOrder.getOrderSource()));
        if (Objects.nonNull(handler)) {
            if (PayStatusEnum.SUCCESS.getCode() == payOrder.getStatus()) {
                handler.orderPayAfterSuccess(payOrder);
            } else {
                handler.orderPayAfterFail(payOrder);
            }
        }
    }

    /**
     * 查询支付状态
     *
     * @param orderSn 订单号
     * @return 支付订单
     */
    @Override
    public PayOrderEntity queryPayStatus(String orderSn) {
        log.info("查询支付状态，订单号：{}", orderSn);
        // 根据订单号查询支付订单，获取支付渠道编码
        PayClient payCLient = getPayStrategy(orderSn);
        return payCLient.queryPayStatus(orderSn);
    }

    /**
     * 关闭支付订单
     *
     * @param orderSn 订单号
     * @return 关闭结果
     */
    @Override
    public Boolean closePayOrder(String orderSn) {
        log.info("关闭支付状态，订单号：{}", orderSn);
        PayClient payCLient = getPayStrategy(orderSn);
        return payCLient.closePayOrder(orderSn);
    }

    /**
     * 根据订单号获取支付策略
     * 提取公共方法
     *
     * @param orderSn 订单号
     * @return 支付策略
     */
    private PayClient getPayStrategy(String orderSn) {
        PayOrderEntity payOrder = payOrderService.getOne(new LambdaQueryWrapper<PayOrderEntity>()
                .eq(PayOrderEntity::getOrderSn, orderSn));
        AssertUtils.notNull(payOrder, ResultCodeEnum.ORDER_NOT_FOUND);
        String channelCode = payOrder.getChannelCode();
        log.info("订单号：{}，支付渠道：{}", orderSn, channelCode);
        return payClientFactory.getPayClient(channelCode);
    }
}