package com.starry.admin.modules.order.service.impl;

import cn.hutool.core.util.StrUtil;
import com.starry.admin.common.exception.CustomException;
import com.starry.admin.modules.custom.module.entity.PlayCustomUserInfoEntity;
import com.starry.admin.modules.custom.service.IPlayCustomUserInfoService;
import com.starry.admin.modules.order.mapper.PlayOrderInfoMapper;
import com.starry.admin.modules.order.module.constant.OrderConstant.BalanceOperationType;
import com.starry.admin.modules.order.module.constant.OrderConstant.OperatorType;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundFlag;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundRecordType;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderRefundState;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderStatus;
import com.starry.admin.modules.order.module.constant.OrderConstant.OrderTriggerSource;
import com.starry.admin.modules.order.module.constant.OrderConstant.ReviewRequirement;
import com.starry.admin.modules.order.module.dto.OrderCompletionContext;
import com.starry.admin.modules.order.module.dto.OrderRefundContext;
import com.starry.admin.modules.order.module.entity.PlayOrderInfoEntity;
import com.starry.admin.modules.order.service.IOrderLifecycleService;
import com.starry.admin.modules.order.service.IPlayOrderRefundInfoService;
import com.starry.admin.modules.weichat.service.WxCustomMpService;
import com.starry.admin.modules.withdraw.entity.EarningsLineEntity;
import com.starry.admin.modules.withdraw.service.IEarningsService;
import com.starry.admin.utils.SecurityUtils;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Slf4j
@Service
public class OrderLifecycleServiceImpl implements IOrderLifecycleService {

    @Resource
    private PlayOrderInfoMapper orderInfoMapper;

    @Resource
    private IEarningsService earningsService;

    @Resource
    private WxCustomMpService wxCustomMpService;

    @Resource
    private IPlayOrderRefundInfoService orderRefundInfoService;

    @Resource
    private IPlayCustomUserInfoService customUserInfoService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void completeOrder(String orderId, OrderCompletionContext context) {
        if (StrUtil.isBlank(orderId)) {
            throw new CustomException("订单ID不能为空");
        }
        PlayOrderInfoEntity order = orderInfoMapper.selectById(orderId);
        if (order == null) {
            throw new CustomException("订单不存在");
        }

        OrderTriggerSource source = context != null && context.getTriggerSource() != null
                ? context.getTriggerSource()
                : OrderTriggerSource.UNKNOWN;
        boolean alreadyCompleted = OrderStatus.COMPLETED.getCode().equals(order.getOrderStatus());
        if (!alreadyCompleted && !OrderStatus.IN_PROGRESS.getCode().equals(order.getOrderStatus())) {
            log.warn("Skip completing order {}, unexpected status {}, source={}", orderId, order.getOrderStatus(), source.getCode());
            throw new CustomException("订单状态异常，无法完成");
        }

        LocalDateTime now = LocalDateTime.now();
        LocalDateTime endTime = order.getOrderEndTime() != null ? order.getOrderEndTime() : now;

        boolean statusUpdated = false;
        if (!alreadyCompleted) {
            PlayOrderInfoEntity update = new PlayOrderInfoEntity(orderId, OrderStatus.COMPLETED.getCode());
            update.setOrderEndTime(endTime);
            orderInfoMapper.updateById(update);
            statusUpdated = true;
        } else if (order.getOrderEndTime() == null) {
            PlayOrderInfoEntity update = new PlayOrderInfoEntity(orderId, OrderStatus.COMPLETED.getCode());
            update.setOrderEndTime(endTime);
            orderInfoMapper.updateById(update);
            statusUpdated = true;
        }

        PlayOrderInfoEntity latest = orderInfoMapper.selectById(orderId);
        if (latest == null) {
            throw new CustomException("订单不存在");
        }

        boolean earningsCreated = ensureEarnings(latest, source);
        boolean forceNotify = context != null && context.isForceNotify();
        boolean shouldNotify = statusUpdated || (forceNotify && earningsCreated);
        if (shouldNotify) {
            wxCustomMpService.sendOrderFinishMessageAsync(latest);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundOrder(OrderRefundContext context) {
        if (context == null || StrUtil.isBlank(context.getOrderId())) {
            throw new CustomException("订单ID不能为空");
        }
        PlayOrderInfoEntity order = orderInfoMapper.selectById(context.getOrderId());
        if (order == null) {
            throw new CustomException("订单不存在");
        }
        BigDecimal refundAmount = context.getRefundAmount();
        if (refundAmount == null) {
            throw new CustomException("退款金额不能为空");
        }
        if (refundAmount.compareTo(BigDecimal.ZERO) < 0) {
            throw new CustomException("退款金额不能小于0");
        }
        BigDecimal finalAmount = order.getFinalAmount() == null ? BigDecimal.ZERO : order.getFinalAmount();
        if (finalAmount.compareTo(refundAmount) < 0) {
            throw new CustomException("退款金额不能大于支付金额");
        }
        if (OrderStatus.COMPLETED.getCode().equals(order.getOrderStatus())) {
            throw new CustomException("【已完成】的订单无法操作退款");
        }
        if (OrderStatus.CANCELLED.getCode().equals(order.getOrderStatus())) {
            throw new CustomException("【已取消】的订单无法操作退款");
        }
        if (OrderRefundFlag.REFUNDED.getCode().equals(order.getRefundType())) {
            throw new CustomException("每个订单只能退款一次~");
        }

        PlayOrderInfoEntity update = new PlayOrderInfoEntity();
        update.setId(order.getId());
        update.setRefundType(OrderRefundFlag.REFUNDED.getCode());
        update.setOrderStatus(OrderStatus.CANCELLED.getCode());
        update.setRefundAmount(refundAmount);
        orderInfoMapper.updateById(update);

        PlayCustomUserInfoEntity customUser = customUserInfoService.getById(order.getPurchaserBy());
        if (customUser == null) {
            throw new CustomException("顾客信息不存在");
        }
        BigDecimal currentBalance = customUser.getAccountBalance() == null ? BigDecimal.ZERO : customUser.getAccountBalance();
        BigDecimal refundSourceAmount = refundAmount;
        customUserInfoService.updateAccountBalanceById(
                customUser.getId(),
                currentBalance,
                currentBalance.add(refundSourceAmount),
                BalanceOperationType.REFUND.getCode(),
                "订单退款",
                refundSourceAmount,
                BigDecimal.ZERO,
                order.getId());

        OrderRefundRecordType refundRecordType = finalAmount.compareTo(refundAmount) == 0
                ? OrderRefundRecordType.FULL
                : OrderRefundRecordType.PARTIAL;
        String refundByType = StrUtil.isNotBlank(context.getOperatorType())
                ? context.getOperatorType()
                : OperatorType.ADMIN.getCode();
        String refundById = StrUtil.isNotBlank(context.getOperatorId()) ? context.getOperatorId() : SecurityUtils.getUserId();

        orderRefundInfoService.add(
                order.getId(),
                order.getPurchaserBy(),
                order.getAcceptBy(),
                order.getPayMethod(),
                refundRecordType.getCode(),
                refundAmount,
                context.getRefundReason(),
                refundByType,
                refundById,
                OrderRefundState.PROCESSING.getCode(),
                ReviewRequirement.NOT_REQUIRED.getCode());
    }

    private boolean ensureEarnings(PlayOrderInfoEntity order, OrderTriggerSource source) {
        Long existing = earningsService.lambdaQuery()
                .eq(EarningsLineEntity::getTenantId, order.getTenantId())
                .eq(EarningsLineEntity::getOrderId, order.getId())
                .count();
        if (existing != null && existing > 0) {
            return false;
        }
        earningsService.createFromOrder(order);
        Long after = earningsService.lambdaQuery()
                .eq(EarningsLineEntity::getTenantId, order.getTenantId())
                .eq(EarningsLineEntity::getOrderId, order.getId())
                .count();
        if (after == null || after == 0) {
            log.warn("Failed to create earnings line for order {}, source={}", order.getId(), source.getCode());
            return false;
        }
        return true;
    }
}
