package com.ruoyi.web.modules.order.service.impl;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.github.pagehelper.PageInfo;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DictUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.web.modules.account.dto.MemberOrderQueryDTO;
import com.ruoyi.web.modules.account.util.MemberMobileUtils;
import com.ruoyi.web.modules.delivery.service.IShopDeliveryService;
import com.ruoyi.web.modules.order.constant.OrderConstants;
import com.ruoyi.web.modules.order.domain.BizOrder;
import com.ruoyi.web.modules.order.domain.BizOrderItem;
import com.ruoyi.web.modules.order.domain.BizOrderLogisticsTrace;
import com.ruoyi.web.modules.order.dto.OrderDeliveredDTO;
import com.ruoyi.web.modules.order.dto.OrderLogisticsDTO;
import com.ruoyi.web.modules.order.dto.OrderShipDTO;
import com.ruoyi.web.modules.order.dto.SellerOrderQuery;
import com.ruoyi.web.modules.order.mapper.BizOrderItemMapper;
import com.ruoyi.web.modules.order.mapper.BizOrderLogisticsTraceMapper;
import com.ruoyi.web.modules.order.mapper.BizOrderMapper;
import com.ruoyi.web.modules.order.service.IOrderService;
import com.ruoyi.web.modules.order.support.OrderSupport;
import com.ruoyi.web.modules.order.vo.OrderDetailItemVO;
import com.ruoyi.web.modules.order.vo.OrderDetailVO;
import com.ruoyi.web.modules.order.vo.OrderListItemVO;
import com.ruoyi.web.modules.order.vo.OrderListRowVO;
import com.ruoyi.web.modules.order.vo.OrderTraceVO;
import com.ruoyi.web.modules.order.vo.SellerShipBadgeVO;

@Service
public class OrderServiceImpl implements IOrderService
{
    @Autowired
    private BizOrderMapper orderMapper;

    @Autowired
    private BizOrderItemMapper orderItemMapper;

    @Autowired
    private BizOrderLogisticsTraceMapper traceMapper;

    @Autowired
    private IShopDeliveryService shopDeliveryService;

    @Override
    public List<OrderListRowVO> selectSellerList(Long shopId, SellerOrderQuery query)
    {
        if (query == null)
        {
            query = new SellerOrderQuery();
        }
        List<BizOrder> orders = orderMapper.selectSellerList(shopId, query);
        if (orders.isEmpty())
        {
            return Collections.emptyList();
        }
        List<Long> orderIds = orders.stream().map(BizOrder::getOrderId).collect(Collectors.toList());
        Map<Long, List<BizOrderItem>> itemMap = groupItems(orderItemMapper.selectByOrderIds(orderIds));
        List<OrderListRowVO> rows = new ArrayList<>(orders.size());
        for (BizOrder order : orders)
        {
            rows.add(toListRow(order, itemMap.get(order.getOrderId()), true));
        }
        return rows;
    }

    @Override
    public OrderDetailVO selectSellerDetail(Long shopId, Long orderId)
    {
        BizOrder order = OrderSupport.assertOrderBelongsToShop(orderMapper.selectById(orderId), shopId);
        return buildDetail(order, false);
    }

    @Override
    public OrderDetailVO selectSellerShipDetail(Long shopId, Long orderId)
    {
        BizOrder order = OrderSupport.assertOrderBelongsToShop(orderMapper.selectById(orderId), shopId);
        OrderSupport.assertShipModuleVisible(order);
        return buildDetail(order, false);
    }

    @Override
    public SellerShipBadgeVO countSellerShipBadges(Long shopId)
    {
        SellerShipBadgeVO badges = new SellerShipBadgeVO();
        badges.setPendingShipCount(orderMapper.countShopByStatus(shopId, OrderConstants.STATUS_PENDING_SHIP));
        badges.setShippedCount(orderMapper.countShopByStatus(shopId, OrderConstants.STATUS_SHIPPED));
        badges.setCompletedCount(orderMapper.countShopByStatus(shopId, OrderConstants.STATUS_COMPLETED));
        return badges;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void shipOrder(Long shopId, Long orderId, OrderShipDTO dto, String operator)
    {
        BizOrder order = OrderSupport.assertOrderBelongsToShop(orderMapper.selectById(orderId), shopId);
        OrderSupport.assertPendingShip(order);
        OrderSupport.validateShipDto(dto);

        BizOrder update = new BizOrder();
        update.setOrderId(orderId);
        update.setShopId(shopId);
        update.setDeliveryType(dto.getDeliveryType());
        update.setShipTime(dto.getShipTime());
        update.setShipRemark(dto.getShipRemark());
        update.setUpdateBy(operator);
        OrderSupport.applyShipperSnapshot(update, shopDeliveryService.getByShopId(shopId));
        if (OrderConstants.DELIVERY_LOGISTICS.equals(dto.getDeliveryType()))
        {
            update.setLogisticsCompany(dto.getLogisticsCompany());
            update.setTrackingNo(dto.getTrackingNo());
            update.setVehicleNo(null);
            update.setCourierName(null);
            update.setCourierMobile(null);
        }
        else
        {
            update.setVehicleNo(dto.getVehicleNo());
            update.setCourierName(dto.getCourierName());
            update.setCourierMobile(dto.getCourierMobile());
            update.setLogisticsCompany(null);
            update.setTrackingNo(null);
        }
        if (orderMapper.updateForShip(update) == 0)
        {
            throw new ServiceException(OrderConstants.MSG_CANNOT_SHIP);
        }
        insertTrace(orderId, OrderConstants.TRACE_SHIPPED, dto.getShipTime(),
                OrderSupport.buildShipTraceContent(dto), operator);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void appendLogistics(Long shopId, Long orderId, OrderLogisticsDTO dto, String operator)
    {
        BizOrder order = OrderSupport.assertOrderBelongsToShop(orderMapper.selectById(orderId), shopId);
        OrderSupport.assertShipped(order);
        OrderSupport.validateTraceDto(dto);
        insertTrace(orderId, OrderConstants.TRACE_TRANSIT, dto.getTraceTime(), dto.getContent(), operator);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void registerDelivered(Long shopId, Long orderId, OrderDeliveredDTO dto, String operator)
    {
        BizOrder order = OrderSupport.assertOrderBelongsToShop(orderMapper.selectById(orderId), shopId);
        OrderSupport.assertShipped(order);
        OrderSupport.validateDeliveredDto(dto);
        insertTrace(orderId, OrderConstants.TRACE_DELIVERED, dto.getTraceTime(), dto.getContent(), operator);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteClosedOrder(Long shopId, Long orderId, String operator)
    {
        BizOrder order = OrderSupport.assertOrderBelongsToShop(orderMapper.selectById(orderId), shopId);
        OrderSupport.assertClosed(order);
        if (orderMapper.updateForDelete(orderId, shopId, operator) == 0)
        {
            throw new ServiceException(OrderConstants.MSG_CANNOT_DELETE);
        }
    }

    @Override
    public boolean hasUnfinishedOrdersByShop(Long shopId)
    {
        return shopId != null && orderMapper.existsUnfinishedByShop(shopId) > 0;
    }

    @Override
    public boolean hasUnfinishedOrdersByMerchant(Long merchantId)
    {
        return merchantId != null && orderMapper.existsUnfinishedByMerchant(merchantId) > 0;
    }

    @Override
    public TableDataInfo pageOrdersByMember(Long memberId, MemberOrderQueryDTO query)
    {
        if (query == null)
        {
            query = new MemberOrderQueryDTO();
        }
        List<BizOrder> orders = orderMapper.selectMemberList(memberId, query);
        TableDataInfo data = new TableDataInfo();
        data.setCode(200);
        data.setMsg("查询成功");
        if (orders.isEmpty())
        {
            data.setRows(Collections.emptyList());
            data.setTotal(0L);
            return data;
        }
        List<Long> orderIds = orders.stream().map(BizOrder::getOrderId).collect(Collectors.toList());
        Map<Long, List<BizOrderItem>> itemMap = groupItems(orderItemMapper.selectByOrderIds(orderIds));
        List<OrderListRowVO> rows = new ArrayList<>(orders.size());
        for (BizOrder order : orders)
        {
            rows.add(toListRow(order, itemMap.get(order.getOrderId()), false));
        }
        data.setRows(rows);
        data.setTotal(new PageInfo<>(orders).getTotal());
        return data;
    }

    @Override
    public OrderDetailVO getOrderDetail(Long memberId, Long orderId)
    {
        BizOrder order = orderMapper.selectByIdAndMember(orderId, memberId);
        if (order == null)
        {
            throw new ServiceException(OrderConstants.MSG_NOT_FOUND);
        }
        return buildDetail(order, false);
    }

    private OrderDetailVO buildDetail(BizOrder order, boolean maskMobile)
    {
        OrderDetailVO vo = new OrderDetailVO();
        vo.setOrderId(order.getOrderId());
        vo.setOrderNo(order.getOrderNo());
        vo.setCreateTime(order.getCreateTime());
        vo.setOrderStatus(order.getOrderStatus());
        vo.setOrderStatusLabel(dictLabel(OrderConstants.DICT_ORDER_STATUS, order.getOrderStatus()));
        vo.setPayAmount(order.getPayAmount());
        vo.setFreightAmount(order.getFreightAmount());
        vo.setMemberNickName(order.getMemberNickName());
        vo.setDeliveryType(order.getDeliveryType());
        vo.setDeliveryTypeLabel(dictLabel(OrderConstants.DICT_DELIVERY_TYPE, order.getDeliveryType()));
        vo.setPayStatus(order.getPayStatus());
        vo.setPayTime(order.getPayTime());
        vo.setShipTime(order.getShipTime());
        vo.setFinishTime(order.getFinishTime());
        vo.setConsigneeName(order.getConsigneeName());
        vo.setConsigneeMobile(maskMobile ? MemberMobileUtils.mask(order.getConsigneeMobile())
                : order.getConsigneeMobile());
        vo.setConsigneeAddress(order.getConsigneeAddress());
        vo.setLogisticsCompany(order.getLogisticsCompany());
        vo.setTrackingNo(order.getTrackingNo());
        vo.setVehicleNo(order.getVehicleNo());
        vo.setCourierName(order.getCourierName());
        vo.setCourierMobile(order.getCourierMobile());
        vo.setShipRemark(order.getShipRemark());
        vo.setShipperName(order.getShipperName());
        vo.setShipperMobile(order.getShipperMobile());
        vo.setShipRegionName(order.getShipRegionName());
        vo.setShipDetailAddress(order.getShipDetailAddress());
        vo.setItems(toDetailItems(orderItemMapper.selectByOrderId(order.getOrderId())));
        vo.setTraceList(toTraceVos(traceMapper.selectByOrderId(order.getOrderId())));
        if (OrderConstants.STATUS_CLOSED.equals(order.getOrderStatus())
                || OrderConstants.STATUS_DELETED.equals(order.getOrderStatus()))
        {
            vo.setCloseType(order.getCloseType());
            vo.setCloseTypeLabel(dictLabel(OrderConstants.DICT_CLOSE_TYPE, order.getCloseType()));
            vo.setCloseReason(order.getCloseReason());
        }
        return vo;
    }

    private OrderListRowVO toListRow(BizOrder order, List<BizOrderItem> items, boolean maskMobile)
    {
        OrderListRowVO vo = new OrderListRowVO();
        vo.setOrderId(order.getOrderId());
        vo.setOrderNo(order.getOrderNo());
        vo.setCreateTime(order.getCreateTime());
        vo.setOrderStatus(order.getOrderStatus());
        vo.setOrderStatusLabel(dictLabel(OrderConstants.DICT_ORDER_STATUS, order.getOrderStatus()));
        vo.setPayAmount(order.getPayAmount());
        vo.setFreightAmount(order.getFreightAmount());
        vo.setMemberNickName(order.getMemberNickName());
        vo.setConsigneeName(order.getConsigneeName());
        vo.setConsigneeMobile(maskMobile ? MemberMobileUtils.mask(order.getConsigneeMobile())
                : order.getConsigneeMobile());
        vo.setConsigneeAddress(order.getConsigneeAddress());
        vo.setDeliveryType(order.getDeliveryType());
        vo.setDeliveryTypeLabel(dictLabel(OrderConstants.DICT_DELIVERY_TYPE, order.getDeliveryType()));
        List<BizOrderItem> safeItems = items == null ? Collections.emptyList() : items;
        vo.setItemCount(safeItems.size());
        vo.setItems(toListItems(safeItems));
        return vo;
    }

    private List<OrderListItemVO> toListItems(List<BizOrderItem> items)
    {
        List<OrderListItemVO> list = new ArrayList<>(items.size());
        for (BizOrderItem item : items)
        {
            OrderListItemVO vo = new OrderListItemVO();
            vo.setGoodsImage(item.getGoodsImage());
            vo.setGoodsName(item.getGoodsName());
            vo.setGoodsSpec(item.getGoodsSpec());
            vo.setQuantity(item.getQuantity());
            vo.setUnitPrice(item.getUnitPrice());
            list.add(vo);
        }
        return list;
    }

    private List<OrderDetailItemVO> toDetailItems(List<BizOrderItem> items)
    {
        List<OrderDetailItemVO> list = new ArrayList<>(items.size());
        for (BizOrderItem item : items)
        {
            OrderDetailItemVO vo = new OrderDetailItemVO();
            vo.setItemId(item.getItemId());
            vo.setGoodsImage(item.getGoodsImage());
            vo.setGoodsName(item.getGoodsName());
            vo.setGoodsSpec(item.getGoodsSpec());
            vo.setUnitPrice(item.getUnitPrice());
            vo.setQuantity(item.getQuantity());
            vo.setLineAmount(item.getLineAmount());
            list.add(vo);
        }
        return list;
    }

    private List<OrderTraceVO> toTraceVos(List<BizOrderLogisticsTrace> traces)
    {
        if (traces == null || traces.isEmpty())
        {
            return Collections.emptyList();
        }
        List<OrderTraceVO> list = new ArrayList<>(traces.size());
        for (BizOrderLogisticsTrace trace : traces)
        {
            OrderTraceVO vo = new OrderTraceVO();
            vo.setTraceType(trace.getTraceType());
            vo.setTraceTypeLabel(dictLabel(OrderConstants.DICT_TRACE_TYPE, trace.getTraceType()));
            vo.setTraceTime(trace.getTraceTime());
            vo.setContent(trace.getContent());
            vo.setCreateBy(trace.getCreateBy());
            list.add(vo);
        }
        return list;
    }

    private Map<Long, List<BizOrderItem>> groupItems(List<BizOrderItem> items)
    {
        if (items == null || items.isEmpty())
        {
            return Collections.emptyMap();
        }
        Map<Long, List<BizOrderItem>> map = new HashMap<>();
        for (BizOrderItem item : items)
        {
            map.computeIfAbsent(item.getOrderId(), k -> new ArrayList<>()).add(item);
        }
        return map;
    }

    private void insertTrace(Long orderId, String traceType, java.util.Date traceTime, String content, String operator)
    {
        BizOrderLogisticsTrace trace = new BizOrderLogisticsTrace();
        trace.setOrderId(orderId);
        trace.setTraceType(traceType);
        trace.setTraceTime(traceTime);
        trace.setContent(StringUtils.isEmpty(content) ? "" : content);
        trace.setCreateBy(operator);
        traceMapper.insertTrace(trace);
    }

    private String dictLabel(String dictType, String value)
    {
        if (StringUtils.isEmpty(value))
        {
            return null;
        }
        try
        {
            return DictUtils.getDictLabel(dictType, value);
        }
        catch (Exception ex)
        {
            return value;
        }
    }
}
