package com.jeesharp.business.modules.finance.profitsharing.application;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.finance.payorder.entity.PayOrder;
import com.jeesharp.business.modules.finance.payorder.service.PayOrderService;
import com.jeesharp.business.modules.finance.payrefund.entity.PayRefundOrder;
import com.jeesharp.business.modules.finance.payrefund.service.PayRefundOrderService;
import com.jeesharp.business.modules.finance.profitsharing.service.ProfitSharingOrderService;
import com.jeesharp.business.modules.finance.profitsharing.convert.ProfitSharingReturnConvert;
import com.jeesharp.business.modules.finance.profitsharing.entity.ProfitSharingOrder;
import com.jeesharp.business.modules.finance.profitsharing.entity.ProfitSharingReturn;
import com.jeesharp.business.modules.finance.profitsharing.enums.ProfitSharingReturnStatusEnum;
import com.jeesharp.business.modules.finance.profitsharing.service.ProfitSharingReturnService;
import com.jeesharp.business.modules.finance.profitsharing.vo.ProfitSharingReturnInfoVO;
import com.jeesharp.business.modules.finance.profitsharing.vo.ProfitSharingReturnListVO;
import com.jeesharp.business.modules.finance.profitsharing.vo.ProfitSharingReturnQueryVO;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordBizSceneEnum;
import com.jeesharp.business.modules.finance.settle.support.PaySettleBizOrderResolver;
import com.jeesharp.business.modules.pay.api.PayProfitSharingReturnChannelApi;
import com.jeesharp.business.modules.pay.profitsharing.dto.PayProfitSharingReturnRequestDTO;
import com.jeesharp.business.modules.pay.profitsharing.dto.PayProfitSharingReturnResultDTO;
import com.jeesharp.framework.exception.BizErrorException;
import com.jeesharp.framework.pojo.Page;
import com.jeesharp.framework.pojo.query.PageRespVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 分账回退应用服务：场景 C 退款前回退商户分账金额，并提供查询与重试。
 *
 * @author jeesharp
 * @since 2026-06-11
 */
@Slf4j
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class ProfitSharingReturnApplicationService {

    private final ProfitSharingReturnService profitSharingReturnService;
    private final PayProfitSharingReturnChannelApi payProfitSharingReturnChannelApi;
    private final PaySettleBizOrderResolver paySettleBizOrderResolver;
    private final PayRefundOrderService payRefundOrderService;
    private final PayOrderService payOrderService;
    private final ProfitSharingOrderService profitSharingOrderService;

    public ProfitSharingReturnInfoVO get(String id) {
        ProfitSharingReturn entity = profitSharingReturnService.get(id);
        if (entity == null) {
            throw new BizErrorException("分账回退单不存在");
        }
        ProfitSharingReturnInfoVO vo = ProfitSharingReturnConvert.INSTANCE.toInfoVO(entity);
        enrichDisplayFields(vo, entity);
        return vo;
    }

    public PageRespVO<ProfitSharingReturnListVO> findPage(Page<ProfitSharingReturn> page, ProfitSharingReturnQueryVO query) {
        resolveQueryAssociations(query);
        Page<ProfitSharingReturn> result = profitSharingReturnService.findPage(page, query);
        PageRespVO<ProfitSharingReturnListVO> pageVo = ProfitSharingReturnConvert.INSTANCE.toPageRespVO(result);
        enrichListDisplayFields(pageVo.getRecords(), result.getRecords());
        return pageVo;
    }

    /**
     * 为退款单创建并执行分账回退（幂等）；回退金额为 0 时直接视为成功。
     *
     * @return 是否已可继续微信退款（回退 SUCCESS 或无需回退）
     */
    @Transactional(readOnly = false)
    public boolean ensureReturnForRefund(String payRefundOrderId) {
        if (StrUtil.isBlank(payRefundOrderId)) {
            return true;
        }
        PayRefundOrder refund = payRefundOrderService.get(payRefundOrderId.trim());
        if (refund == null) {
            return true;
        }
        PayOrder payOrder = payOrderService.get(refund.getFinancePayOrderId());
        if (payOrder == null) {
            return true;
        }
        ProfitSharingOrder sharing = profitSharingOrderService.findByMallOrderId(refund.getMallOrderId());
        if (sharing == null) {
            return true;
        }
        return ensureReturnForRefund(sharing, refund, payOrder);
    }

    /**
     * 为退款单创建并执行分账回退（幂等）；回退金额为 0 时直接视为成功。
     *
     * @return 是否已可继续微信退款（回退 SUCCESS 或无需回退）
     */
    @Transactional(readOnly = false)
    public boolean ensureReturnForRefund(ProfitSharingOrder sharing, PayRefundOrder refund, PayOrder payOrder) {
        if (sharing == null || refund == null) {
            return true;
        }
        ProfitSharingReturn existing = profitSharingReturnService.findByPayRefundOrderId(refund.getId());
        if (existing != null) {
            refreshReturnStatus(existing, payOrder.getTenantId());
            return ProfitSharingReturnStatusEnum.SUCCESS.getValue().equals(existing.getStatus());
        }

        int returnFen = calcReturnFen(sharing, refund.getRefundAmount());
        if (returnFen < 1) {
            return true;
        }

        ProfitSharingReturn entity = buildReturnEntity(sharing, refund, returnFen);
        profitSharingReturnService.createReturnOrder(entity);
        executeChannelReturn(entity, payOrder.getTenantId());
        return ProfitSharingReturnStatusEnum.SUCCESS.getValue().equals(entity.getStatus());
    }

    @Transactional(readOnly = false)
    public void retry(String returnOrderId) {
        ProfitSharingReturn entity = profitSharingReturnService.get(returnOrderId);
        if (entity == null) {
            throw new BizErrorException("分账回退单不存在");
        }
        if (ProfitSharingReturnStatusEnum.SUCCESS.getValue().equals(entity.getStatus())) {
            return;
        }
        String platformTenantId = entity.getTenantId();
        if (ProfitSharingReturnStatusEnum.PROCESSING.getValue().equals(entity.getStatus())) {
            refreshReturnStatus(entity, platformTenantId);
            if (ProfitSharingReturnStatusEnum.SUCCESS.getValue().equals(entity.getStatus())) {
                return;
            }
        }
        entity.setRetryCount(entity.getRetryCount() == null ? 1 : entity.getRetryCount() + 1);
        executeChannelReturn(entity, platformTenantId);
    }

    private void executeChannelReturn(ProfitSharingReturn entity, String platformTenantId) {
        PayProfitSharingReturnRequestDTO request = new PayProfitSharingReturnRequestDTO();
        request.setPlatformTenantId(platformTenantId);
        request.setSubMchId(entity.getSubMchId());
        request.setWxOrderId(entity.getWxOrderId());
        request.setOutOrderNo(entity.getSharingNo());
        request.setOutReturnNo(entity.getOutReturnNo());
        request.setReturnMchId(entity.getReturnMchId());
        request.setAmountFen(entity.getReturnAmountFen());
        request.setDescription(entity.getDescription());
        request.setTransactionId(entity.getTransactionId());

        PayProfitSharingReturnResultDTO result = payProfitSharingReturnChannelApi.applyReturn(request);
        entity.setRawRequest(result.getRawResponse());
        entity.setRawResponse(result.getRawResponse());
        if (result.isSuccess()) {
            entity.setWxReturnNo(result.getWxReturnNo());
            entity.setWxResult(result.getResult());
            entity.setFailReason(result.getFailReason());
            entity.setErrorCode(null);
            entity.setErrorMsg(null);
            applyWxResult(entity, result.getResult());
        } else {
            entity.setStatus(ProfitSharingReturnStatusEnum.FAIL.getValue());
            entity.setErrorCode(result.getErrorCode());
            entity.setErrorMsg(StrUtil.blankToDefault(result.getErrorMsg(), "微信分账回退受理失败"));
        }
        profitSharingReturnService.updateReturnOrder(entity);
    }

    private void refreshReturnStatus(ProfitSharingReturn entity, String platformTenantId) {
        PayProfitSharingReturnResultDTO result = payProfitSharingReturnChannelApi.queryByOutReturnNo(
                platformTenantId, entity.getSubMchId(), entity.getOutReturnNo());
        if (!result.isSuccess()) {
            entity.setErrorMsg(result.getErrorMsg());
            profitSharingReturnService.updateReturnOrder(entity);
            return;
        }
        entity.setWxReturnNo(result.getWxReturnNo());
        entity.setWxResult(result.getResult());
        entity.setFailReason(result.getFailReason());
        entity.setRawResponse(result.getRawResponse());
        applyWxResult(entity, result.getResult());
        profitSharingReturnService.updateReturnOrder(entity);
    }

    private static void applyWxResult(ProfitSharingReturn entity, String wxResult) {
        if ("SUCCESS".equalsIgnoreCase(wxResult)) {
            entity.setStatus(ProfitSharingReturnStatusEnum.SUCCESS.getValue());
            entity.setReturnTime(LocalDateTime.now());
            return;
        }
        if ("FAILED".equalsIgnoreCase(wxResult)) {
            entity.setStatus(ProfitSharingReturnStatusEnum.FAIL.getValue());
            return;
        }
        entity.setStatus(ProfitSharingReturnStatusEnum.PROCESSING.getValue());
    }

    private ProfitSharingReturn buildReturnEntity(ProfitSharingOrder sharing, PayRefundOrder refund, int returnFen) {
        ProfitSharingReturn entity = new ProfitSharingReturn();
        entity.setReturnNo("PR" + IdUtil.getSnowflakeNextIdStr());
        entity.setOutReturnNo(IdUtil.fastSimpleUUID());
        entity.setProfitSharingOrderId(sharing.getId());
        entity.setPayRefundOrderId(refund.getId());
        entity.setMallOrderId(sharing.getMallOrderId());
        entity.setSubMchId(sharing.getSubMchId());
        entity.setSharingNo(sharing.getSharingNo());
        entity.setWxOrderId(sharing.getWxOrderId());
        entity.setTransactionId(sharing.getTransactionId());
        entity.setReturnMchId(sharing.getSubMchId());
        entity.setReturnAmountFen(returnFen);
        entity.setStatus(ProfitSharingReturnStatusEnum.PENDING.getValue());
        entity.setDescription("订单退款分账回退-" + sharing.getMallOrderId());
        entity.setRetryCount(0);
        entity.setTenantId(sharing.getTenantId());
        return entity;
    }

    private int calcReturnFen(ProfitSharingOrder sharing, BigDecimal refundAmount) {
        if (sharing.getSharingAmountFen() == null || sharing.getSharingAmountFen() < 1) {
            return 0;
        }
        BigDecimal payAmount = sharing.getPayAmount() == null ? BigDecimal.ZERO : sharing.getPayAmount();
        if (payAmount.compareTo(BigDecimal.ZERO) <= 0 || refundAmount == null || refundAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return 0;
        }
        int alreadyReturned = profitSharingReturnService.sumSuccessReturnFenByProfitSharingOrderId(sharing.getId());
        int remainFen = Math.max(sharing.getSharingAmountFen() - alreadyReturned, 0);
        if (remainFen < 1) {
            return 0;
        }
        int proportional = refundAmount.multiply(BigDecimal.valueOf(sharing.getSharingAmountFen()))
                .divide(payAmount, 0, RoundingMode.HALF_UP)
                .intValue();
        return Math.min(Math.max(proportional, 0), remainFen);
    }

    private void resolveQueryAssociations(ProfitSharingReturnQueryVO query) {
        if (query == null || StrUtil.isBlank(query.getOrderNo()) || StrUtil.isNotBlank(query.getMallOrderId())) {
            return;
        }
        String orderId = paySettleBizOrderResolver.findFirstOrderIdByOrderNo(
                PayRecordBizSceneEnum.MALL_ORDER.getCode(), query.getOrderNo());
        if (StrUtil.isNotBlank(orderId)) {
            query.setMallOrderId(orderId);
        }
    }

    private void enrichListDisplayFields(List<ProfitSharingReturnListVO> voList, List<ProfitSharingReturn> entities) {
        if (voList == null || entities == null || voList.isEmpty()) {
            return;
        }
        Map<String, String> orderNoMap = loadOrderNoMap(entities);
        for (int i = 0; i < voList.size() && i < entities.size(); i++) {
            ProfitSharingReturnListVO vo = voList.get(i);
            ProfitSharingReturn entity = entities.get(i);
            vo.setOrderNo(orderNoMap.get(entity.getMallOrderId()));
            vo.setSharingNo(entity.getSharingNo());
        }
    }

    private void enrichDisplayFields(ProfitSharingReturnInfoVO vo, ProfitSharingReturn entity) {
        if (vo == null || entity == null) {
            return;
        }
        vo.setSharingNo(entity.getSharingNo());
        if (StrUtil.isNotBlank(entity.getMallOrderId())) {
            var order = paySettleBizOrderResolver.getOrder(PayRecordBizSceneEnum.MALL_ORDER.getCode(),
                    entity.getMallOrderId());
            if (order != null) {
                vo.setOrderNo(order.getOrderNo());
            }
        }
    }

    private Map<String, String> loadOrderNoMap(List<ProfitSharingReturn> entities) {
        if (entities == null || entities.isEmpty()) {
            return new HashMap<>();
        }
        return paySettleBizOrderResolver.mapOrderNoByIds(PayRecordBizSceneEnum.MALL_ORDER.getCode(), entities.stream()
                .map(ProfitSharingReturn::getMallOrderId)
                .filter(StrUtil::isNotBlank)
                .toList());
    }
}
