package com.snack.server.module.order.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.snack.server.common.ResultCode;
import com.snack.server.exception.BusinessException;
import com.snack.server.module.address.entity.Address;
import com.snack.server.module.address.service.AddressService;
import com.snack.server.module.cart.entity.Cart;
import com.snack.server.module.cart.service.CartService;
import com.snack.server.module.order.constant.OrderConstant;
import com.snack.server.module.order.dto.req.OrderPageReq;
import com.snack.server.module.order.dto.req.OrderSubmitReq;
import com.snack.server.module.order.entity.Order;
import com.snack.server.module.order.entity.OrderItem;
import com.snack.server.module.order.enums.OrderStatusEnum;
import com.snack.server.module.order.enums.PayChannelEnum;
import com.snack.server.module.order.mapper.OrderItemMapper;
import com.snack.server.module.order.mapper.OrderMapper;
import com.snack.server.module.order.service.OrderService;
import com.snack.server.module.order.vo.OrderDetailVO;
import com.snack.server.module.order.vo.OrderItemVO;
import com.snack.server.module.product.entity.Product;
import com.snack.server.module.product.entity.ProductSku;
import com.snack.server.module.product.mapper.ProductMapper;
import com.snack.server.module.product.mapper.ProductSkuMapper;
import com.snack.server.module.product.service.ProductService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;

/**
 * 订单业务实现
 *
 * 关键设计：
 * 1. 状态机：所有状态流转都校验 OrderStatusEnum.canTransition
 * 2. 状态条件更新：UPDATE ... WHERE status = #{fromStatus}，防止并发覆盖
 * 3. 事务一致性：扣库存 / 写订单 / 删购物车必须全部成功或全部回滚
 * 4. 商品信息快照：订单项冗余商品名/图片/价格，防商品改名
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {

    private final OrderMapper orderMapper;
    private final OrderItemMapper orderItemMapper;
    private final ProductMapper productMapper;
    private final ProductSkuMapper productSkuMapper;
    private final ProductService productService;
    private final CartService cartService;
    private final AddressService addressService;

    // ==================== 提交订单 ====================

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String submitOrder(OrderSubmitReq req) {
        Long userId = StpUtil.getLoginIdAsLong();

        // 1. 校验收货地址
        Address address = addressService.getEntityById(req.getAddressId());
        if (address == null || !address.getUserId().equals(userId)) {
            throw new BusinessException(1000, "收货地址无效");
        }

        // 2. 查询 SKU / 商品（一次性查完，避免循环查询）
        List<Long> skuIds = req.getItems().stream()
                .map(OrderSubmitReq.OrderItemReq::getSkuId)
                .toList();
        List<ProductSku> skus = productSkuMapper.selectByIds(skuIds);
        Map<Long, ProductSku> skuMap = skus.stream()
                .collect(Collectors.toMap(ProductSku::getId, s -> s));
        if (skuMap.size() != skuIds.size()) {
            throw new BusinessException(1000, "部分 SKU 不存在");
        }
        Set<Long> productIds = skus.stream().map(ProductSku::getProductId).collect(Collectors.toSet());
        Map<Long, Product> productMap = productMapper.selectByIds(productIds).stream()
                .collect(Collectors.toMap(Product::getId, p -> p));

        // 3. 计算金额 + 构造 OrderItem 列表
        BigDecimal totalAmount = BigDecimal.ZERO;
        List<OrderItem> orderItems = new ArrayList<>();
        for (OrderSubmitReq.OrderItemReq itemReq : req.getItems()) {
            ProductSku sku = skuMap.get(itemReq.getSkuId());
            Product product = productMap.get(sku.getProductId());

            // 校验商品状态
            if (product == null || product.getStatus() == null || product.getStatus() == 0) {
                throw new BusinessException(ResultCode.PRODUCT_OFF_SHELF,
                        "商品【" + (product == null ? "?" : product.getName()) + "】已下架");
            }
            // 校验库存
            if (sku.getStock() == null || sku.getStock() < itemReq.getQuantity()) {
                throw new BusinessException(ResultCode.STOCK_INSUFFICIENT,
                        "商品【" + product.getName() + " / " + sku.getSkuName() + "】库存不足");
            }
            // 校验数量
            if (itemReq.getQuantity() <= 0) {
                throw new BusinessException(1000, "购买数量必须大于 0");
            }

            BigDecimal subtotal = sku.getPrice().multiply(BigDecimal.valueOf(itemReq.getQuantity()));
            totalAmount = totalAmount.add(subtotal);

            // 快照（防商品改名 / 改价）
            OrderItem item = new OrderItem();
            item.setProductId(product.getId());
            item.setProductName(product.getName());
            item.setProductImage(product.getMainImage());
            item.setSkuId(sku.getId());
            item.setSkuName(sku.getSkuName());
            item.setPrice(sku.getPrice());
            item.setQuantity(itemReq.getQuantity());
            item.setTotalAmount(subtotal);
            orderItems.add(item);
        }

        // 4. 运费（满 N 元包邮，可作为配置项）
        BigDecimal freight = totalAmount.compareTo(new BigDecimal("99")) >= 0
                ? BigDecimal.ZERO : OrderConstant.DEFAULT_FREIGHT;
        BigDecimal payAmount = totalAmount.add(freight);

        // 5. 生成订单号
        String orderNo = generateOrderNo();

        // 6. 创建订单主表
        Order order = new Order();
        order.setOrderNo(orderNo);
        order.setUserId(userId);
        order.setTotalAmount(totalAmount);
        order.setFreightAmount(freight);
        order.setDiscountAmount(BigDecimal.ZERO);
        order.setCouponId(null);
        order.setCouponAmount(BigDecimal.ZERO);
        order.setPayAmount(payAmount);
        order.setStatus(OrderStatusEnum.PENDING_PAY.getCode());
        order.setReceiverName(address.getReceiver());
        order.setReceiverPhone(address.getPhone());
        order.setReceiverAddress(buildFullAddress(address));
        order.setRemark(req.getRemark());
        orderMapper.insert(order);
        log.info("创建订单 orderId={} orderNo={} userId={} payAmount={}",
                order.getId(), orderNo, userId, payAmount);

        // 7. 写入订单项
        for (OrderItem it : orderItems) {
            it.setOrderId(order.getId());
            it.setOrderNo(orderNo);
        }
        orderItemMapper.insertBatch(orderItems);

        // 8. 扣减库存（原子操作，失败会抛异常回滚整个事务）
        for (OrderItem it : orderItems) {
            boolean ok = productService.decrStock(it.getSkuId(), it.getQuantity());
            if (!ok) {
                throw new BusinessException(ResultCode.STOCK_INSUFFICIENT,
                        "库存扣减失败：" + it.getProductName() + " / " + it.getSkuName());
            }
        }

        // 9. 删除已下单的购物车项（按 SKU ID）
        cartService.deleteItems(skuIds);

        return orderNo;
    }

    // ==================== 订单详情 ====================

    @Override
    public OrderDetailVO getDetailByCurrentUser(Long orderId) {
        Long userId = StpUtil.getLoginIdAsLong();
        Order order = orderMapper.selectOne(
                new LambdaQueryWrapper<Order>()
                        .eq(Order::getId, orderId)
                        .eq(Order::getUserId, userId)
        );
        if (order == null) {
            throw new BusinessException(1000, "订单不存在或无权访问");
        }
        return enrichDetailVO(order);
    }

    @Override
    public OrderDetailVO getDetailForAdmin(Long orderId) {
        Order order = orderMapper.selectById(orderId);
        if (order == null) {
            throw new BusinessException(1000, "订单不存在");
        }
        return enrichDetailVO(order);
    }

    private OrderDetailVO enrichDetailVO(Order order) {
        OrderDetailVO vo = new OrderDetailVO();
        BeanUtil.copyProperties(order, vo);
        // 状态文本 / 颜色
        OrderStatusEnum statusEnum = OrderStatusEnum.values()[order.getStatus()];
        vo.setStatusText(statusEnum.getDesc());
        vo.setStatusTagType(statusEnum.tagType());

        // 支付渠道文本
        if (StrUtil.isNotBlank(order.getPayChannel())) {
            PayChannelEnum ch = PayChannelEnum.of(order.getPayChannel());
            vo.setPayChannelText(ch == null ? order.getPayChannel() : ch.getDesc());
        }

        // 订单项
        List<OrderItem> items = orderItemMapper.selectList(
                new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, order.getId())
        );
        List<OrderItemVO> itemVOs = items.stream().map(it -> {
            OrderItemVO itemVO = new OrderItemVO();
            BeanUtil.copyProperties(it, itemVO);
            return itemVO;
        }).toList();
        vo.setItems(itemVOs);
        vo.setTotalQuantity(items.stream().mapToInt(OrderItem::getQuantity).sum());
        return vo;
    }

    // ==================== 分页 ====================

    @Override
    public Page<OrderDetailVO> pageCurrentUserOrders(OrderPageReq req) {
        Long userId = StpUtil.getLoginIdAsLong();
        Page<Order> page = new Page<>(req.getCurrent(), req.getSize());
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<Order>()
                .eq(Order::getUserId, userId)
                .eq(req.getStatus() != null, Order::getStatus, req.getStatus())
                .orderByDesc(Order::getId);
        Page<Order> result = orderMapper.selectPage(page, wrapper);
        return toVOPage(result);
    }

    @Override
    public Page<OrderDetailVO> pageAllOrders(OrderPageReq req) {
        Page<Order> page = new Page<>(req.getCurrent(), req.getSize());
        LambdaQueryWrapper<Order> wrapper = new LambdaQueryWrapper<Order>()
                .like(StrUtil.isNotBlank(req.getKeyword()), Order::getOrderNo, req.getKeyword())
                .orderByDesc(Order::getId);
        Page<Order> result = orderMapper.selectPage(page, wrapper);
        return toVOPage(result);
    }

    private Page<OrderDetailVO> toVOPage(Page<Order> page) {
        Page<OrderDetailVO> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        if (CollUtil.isEmpty(page.getRecords())) {
            voPage.setRecords(new ArrayList<>());
            return voPage;
        }
        // 批量查订单项
        List<Long> orderIds = page.getRecords().stream().map(Order::getId).toList();
        Map<Long, List<OrderItem>> itemMap = orderItemMapper.selectByOrderIds(orderIds).stream()
                .collect(Collectors.groupingBy(OrderItem::getOrderId));

        voPage.setRecords(page.getRecords().stream().map(o -> {
            OrderDetailVO vo = new OrderDetailVO();
            BeanUtil.copyProperties(o, vo);
            OrderStatusEnum statusEnum = OrderStatusEnum.values()[o.getStatus()];
            vo.setStatusText(statusEnum.getDesc());
            vo.setStatusTagType(statusEnum.tagType());
            List<OrderItem> items = itemMap.getOrDefault(o.getId(), new ArrayList<>());
            vo.setItems(items.stream().map(it -> {
                OrderItemVO itemVO = new OrderItemVO();
                BeanUtil.copyProperties(it, itemVO);
                return itemVO;
            }).toList());
            vo.setTotalQuantity(items.stream().mapToInt(OrderItem::getQuantity).sum());
            return vo;
        }).toList());
        return voPage;
    }

    // ==================== 状态流转 ====================

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void payOrder(Long orderId) {
        // 兼容旧接口：默认走支付宝（沙箱），实际生产由前端跳转 /api/orders/{id}/pay-url
        handlePaySuccess(orderId, PayChannelEnum.ALIPAY.getCode(), "MOCK-" + System.currentTimeMillis());
    }

    /**
     * 支付成功回调（支付宝/微信异步通知时调用）
     *
     * 注意：真实生产中，支付宝回调要求校验 sign 签名防伪造。
     * 这里只做状态切换和字段写入；签名校验请在 AlipayNotifyController 入口做。
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean handlePaySuccess(Long orderId, String payChannel, String payTradeNo) {
        if (orderId == null || payTradeNo == null) {
            return false;
        }
        int affected = orderMapper.markAsPaid(orderId, payChannel, payTradeNo);
        if (affected == 0) {
            log.warn("订单 {} 支付回调失败：订单不存在或状态非待付款", orderId);
            return false;
        }
        log.info("订单 {} 支付成功 payChannel={} tradeNo={}", orderId, payChannel, payTradeNo);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(Long orderId) {
        Long userId = StpUtil.getLoginIdAsLong();
        Order order = orderMapper.selectOne(
                new LambdaQueryWrapper<Order>()
                        .eq(Order::getId, orderId)
                        .eq(Order::getUserId, userId)
        );
        if (order == null) {
            throw new BusinessException(1000, "订单不存在或无权访问");
        }
        if (order.getStatus() == null
                || !OrderStatusEnum.canTransition(order.getStatus(), OrderStatusEnum.CANCELLED.getCode())) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "当前状态不允许取消");
        }
        Integer fromStatus = order.getStatus();
        Integer toStatus = OrderStatusEnum.CANCELLED.getCode();
        int affected = orderMapper.updateStatusIfMatch(orderId, fromStatus, toStatus);
        if (affected == 0) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "订单状态已变更，请刷新");
        }

        // 释放库存（只有真正占用库存的订单需要释放）
        if (fromStatus != OrderStatusEnum.PENDING_PAY.getCode()) {
            // 待发货 / 待收货 / 已完成的取消 = 退款流程，本示例不释放库存
            // 业务上应记录退款单据，财务流程后再释放
            log.warn("订单 {} 状态 {} 取消，需走退款流程释放库存", order.getOrderNo(), fromStatus);
        } else {
            // 待付款取消 → 释放库存
            List<OrderItem> items = orderItemMapper.selectList(
                    new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderId)
            );
            for (OrderItem item : items) {
                productService.incrStock(item.getSkuId(), item.getQuantity());
            }
            log.info("订单 {} 取消，库存已释放", order.getOrderNo());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmReceive(Long orderId) {
        Long userId = StpUtil.getLoginIdAsLong();
        Order order = orderMapper.selectOne(
                new LambdaQueryWrapper<Order>()
                        .eq(Order::getId, orderId)
                        .eq(Order::getUserId, userId)
        );
        if (order == null) {
            throw new BusinessException(1000, "订单不存在或无权访问");
        }
        if (!OrderStatusEnum.canTransition(order.getStatus(), OrderStatusEnum.COMPLETED.getCode())) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "当前状态不允许确认收货");
        }
        int affected = orderMapper.updateStatusIfMatch(
                orderId,
                OrderStatusEnum.PENDING_RECEIVE.getCode(),
                OrderStatusEnum.COMPLETED.getCode()
        );
        if (affected == 0) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "订单状态已变更，请刷新");
        }
        log.info("订单 {} 确认收货", order.getOrderNo());
    }

    /**
     * 申请退款（同步调用支付宝退款）
     *
     * 真实生产中：
     *   1. 用户点退款 → 服务端调用 alipay.trade.refund 发起退款
     *   2. 支付宝返回异步通知 → handleRefundSuccess() 更新状态
     *
     * 沙箱演示版简化：直接同步标记为已退款（同时记录原因 + 模拟退款号）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyRefund(Long orderId, String reason) {
        Long userId = StpUtil.getLoginIdAsLong();
        Order order = orderMapper.selectOne(
                new LambdaQueryWrapper<Order>()
                        .eq(Order::getId, orderId)
                        .eq(Order::getUserId, userId)
        );
        if (order == null) {
            throw new BusinessException(1000, "订单不存在或无权访问");
        }
        if (order.getStatus() == null
                || !OrderStatusEnum.canTransition(order.getStatus(), OrderStatusEnum.REFUNDED.getCode())) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "当前状态不允许申请退款");
        }
        if (StrUtil.isBlank(reason)) {
            reason = "用户申请退款";
        }
        // 模拟支付宝退款号（沙箱）
        String mockRefundNo = "RF" + System.currentTimeMillis();
        int affected = orderMapper.markAsRefunded(orderId, mockRefundNo, reason);
        if (affected == 0) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "订单状态已变更，请刷新");
        }
        // 释放库存
        List<OrderItem> items = orderItemMapper.selectList(
                new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderId)
        );
        for (OrderItem item : items) {
            productService.incrStock(item.getSkuId(), item.getQuantity());
        }
        log.info("订单 {} 退款成功 refundNo={} reason={}", order.getOrderNo(), mockRefundNo, reason);
    }

    /**
     * 支付宝 / 微信退款成功回调
     * 仅在 status IN (1, 2, 3) 时生效；用于"已发货"或"已完成"订单的异步退款
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean handleRefundSuccess(Long orderId, String refundTradeNo) {
        if (orderId == null || refundTradeNo == null) {
            return false;
        }
        int affected = orderMapper.markAsRefunded(orderId, refundTradeNo, "支付平台回调退款");
        if (affected == 0) {
            log.warn("订单 {} 退款回调失败：状态不符合", orderId);
            return false;
        }
        // 释放库存
        List<OrderItem> items = orderItemMapper.selectList(
                new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderId)
        );
        for (OrderItem item : items) {
            productService.incrStock(item.getSkuId(), item.getQuantity());
        }
        log.info("订单 {} 收到退款回调 refundNo={}", orderId, refundTradeNo);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deliverOrder(Long orderId, String company, String trackingNo) {
        Order order = orderMapper.selectById(orderId);
        if (order == null) {
            throw new BusinessException(1000, "订单不存在");
        }
        if (order.getStatus() == null
                || !OrderStatusEnum.canTransition(order.getStatus(), OrderStatusEnum.PENDING_RECEIVE.getCode())) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "只有待发货订单可以发货");
        }
        if (StrUtil.isBlank(company) || StrUtil.isBlank(trackingNo)) {
            throw new BusinessException(1000, "物流公司和单号不能为空");
        }
        int affected = orderMapper.deliverOrder(orderId, company, trackingNo);
        if (affected == 0) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "订单状态已变更，请刷新");
        }
        log.info("订单 {} 已发货：{} {}", order.getOrderNo(), company, trackingNo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void adminCancelOrder(Long orderId, String reason) {
        Order order = orderMapper.selectById(orderId);
        if (order == null) {
            throw new BusinessException(1000, "订单不存在");
        }
        if (order.getStatus() == null
                || !OrderStatusEnum.canTransition(order.getStatus(), OrderStatusEnum.CANCELLED.getCode())) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "当前状态不允许取消");
        }
        int affected = orderMapper.updateStatusIfMatch(
                orderId, order.getStatus(), OrderStatusEnum.CANCELLED.getCode()
        );
        if (affected == 0) {
            throw new BusinessException(ResultCode.ORDER_STATUS_ERROR, "订单状态已变更，请刷新");
        }
        // 释放库存（仅在订单已扣库存的状态下）
        Integer fromStatus = order.getStatus();
        if (fromStatus != OrderStatusEnum.PENDING_PAY.getCode()) {
            // 已支付订单的取消走退款流程
            orderMapper.updateStatusIfMatch(orderId, OrderStatusEnum.CANCELLED.getCode(), OrderStatusEnum.REFUNDED.getCode());
        }
        // 释放库存
        List<OrderItem> items = orderItemMapper.selectList(
                new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderId)
        );
        for (OrderItem item : items) {
            productService.incrStock(item.getSkuId(), item.getQuantity());
        }
        log.info("管理员取消订单 {} 原因：{}", order.getOrderNo(), reason);
    }

    // ==================== 工具方法 ====================

    @Override
    public long countByStatus(Integer status) {
        return orderMapper.selectCount(
                new LambdaQueryWrapper<Order>().eq(status != null, Order::getStatus, status)
        );
    }

    /**
     * 生成订单号：SN + yyyyMMdd + 6 位随机数 = 共 18 位
     */
    private String generateOrderNo() {
        String date = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        int rand = ThreadLocalRandom.current().nextInt(100000, 999999);
        return OrderConstant.ORDER_NO_PREFIX + date + rand;
    }

    /**
     * 拼接完整收货地址
     */
    private String buildFullAddress(Address a) {
        StringBuilder sb = new StringBuilder();
        if (StrUtil.isNotBlank(a.getProvince())) sb.append(a.getProvince());
        if (StrUtil.isNotBlank(a.getCity()))     sb.append(a.getCity());
        if (StrUtil.isNotBlank(a.getDistrict())) sb.append(a.getDistrict());
        if (StrUtil.isNotBlank(a.getDetail()))    sb.append(a.getDetail());
        return sb.toString();
    }
}
