package com.gxwebsoft.glt.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.gxwebsoft.glt.entity.GltTicketOrder;
import com.gxwebsoft.glt.entity.GltUserTicket;
import com.gxwebsoft.shop.entity.ShopStoreRider;
import com.gxwebsoft.shop.entity.ShopOrder;
import com.gxwebsoft.shop.entity.ShopOrderGoods;
import com.gxwebsoft.shop.entity.ShopUserAddress;
import com.gxwebsoft.shop.service.ShopOrderGoodsService;
import com.gxwebsoft.shop.service.ShopOrderService;
import com.gxwebsoft.shop.service.ShopStoreRiderService;
import com.gxwebsoft.shop.service.ShopUserAddressService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * 送水订单自动派单：
 * - 扫描待配送且未指派配送员的 glt_ticket_order；
 * - 取收货地址坐标（优先 ShopOrder.addressLat/addressLng 订单快照；兜底 addressId -> shop_user_address.lat/lng）；
 * - 在同门店、在线、启用、开启自动派单且有坐标的配送员中，按距离最近优先；
 * - 派单写入 glt_ticket_order.rider_id（即配送员 userId），并同步商城订单 riderId/deliveryStatus（复用 accept 逻辑）。
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GltTicketOrderAutoDispatchService {

    private final GltTicketOrderService gltTicketOrderService;
    private final GltUserTicketService gltUserTicketService;
    private final ShopStoreRiderService shopStoreRiderService;
    private final ShopUserAddressService shopUserAddressService;
    private final ShopOrderService shopOrderService;
    private final ShopOrderGoodsService shopOrderGoodsService;

    /**
     * 自动派单（按距离最近）。
     *
     * @param tenantId  租户ID
     * @param batchSize 单次扫描最大处理条数
     * @return 成功派单数量
     */
    public int autoDispatchWaitingOrders(Integer tenantId, int batchSize) {
        if (tenantId == null || tenantId <= 0) {
            return 0;
        }
        if (batchSize <= 0) {
            batchSize = 50;
        }

        List<GltTicketOrder> waiting = gltTicketOrderService.list(
            new LambdaQueryWrapper<GltTicketOrder>()
                .select(GltTicketOrder::getId, GltTicketOrder::getUserTicketId, GltTicketOrder::getStoreId, GltTicketOrder::getAddressId, GltTicketOrder::getCreateTime)
                .eq(GltTicketOrder::getTenantId, tenantId)
                .eq(GltTicketOrder::getDeleted, 0)
                .and(w -> w.eq(GltTicketOrder::getStatus, 0).or().isNull(GltTicketOrder::getStatus))
                .and(w -> w.eq(GltTicketOrder::getDeliveryStatus, GltTicketOrderService.DELIVERY_STATUS_WAITING)
                    .or().isNull(GltTicketOrder::getDeliveryStatus))
                .and(w -> w.isNull(GltTicketOrder::getRiderId).or().eq(GltTicketOrder::getRiderId, 0))
                .orderByAsc(GltTicketOrder::getCreateTime)
                .orderByAsc(GltTicketOrder::getId)
                .last("limit " + batchSize)
        );

        if (waiting == null || waiting.isEmpty()) {
            return 0;
        }

        int dispatched = 0;
        // 简化实现：按门店分组，避免对同一门店重复查配送员/在途单数。
        Map<Integer, CandidatePool> poolByStoreId = new HashMap<>();

        for (GltTicketOrder order : waiting) {
            if (order == null || order.getId() == null) {
                continue;
            }

            LngLat receiver = resolveReceiverLngLat(tenantId, order);
            if (receiver == null) {
                log.debug("自动派单跳过：无法获取收货坐标 - tenantId={}, ticketOrderId={}, addressId={}",
                    tenantId, order.getId(), order.getAddressId());
                continue;
            }

            Integer storeId = order.getStoreId();
            int storeKey = storeId == null ? -1 : storeId;
            CandidatePool pool = poolByStoreId.computeIfAbsent(storeKey, k -> buildCandidatePool(tenantId, storeId));
            if (pool == null || pool.candidates.isEmpty()) {
                log.debug("自动派单跳过：无可用配送员 - tenantId={}, ticketOrderId={}, storeId={}",
                    tenantId, order.getId(), storeId);
                continue;
            }

            ShopStoreRider best = pickNearest(pool, receiver);
            if (best == null || best.getUserId() == null || best.getUserId() <= 0) {
                log.debug("自动派单跳过：未选出配送员 - tenantId={}, ticketOrderId={}, storeId={}",
                    tenantId, order.getId(), storeId);
                continue;
            }

            try {
                // 复用“接单”的原子更新 + 同步商城订单逻辑；这里的 riderId 即配送员 userId。
                gltTicketOrderService.accept(order.getId(), best.getUserId(), tenantId);
                dispatched++;

                // 派单成功后，更新 pool 中该骑手的在途数（便于同批次后续订单使用 maxOnhandOrders）
                pool.onhandCount.merge(best.getUserId(), 1, Integer::sum);
            } catch (Exception e) {
                // accept 本身包含幂等条件（riderId 为空才会写入）；此处吞掉异常，避免一单影响整批。
                log.warn("自动派单失败 - tenantId={}, ticketOrderId={}, storeId={}, riderUserId={}",
                    tenantId, order.getId(), storeId, best.getUserId(), e);
            }
        }

        return dispatched;
    }

    private CandidatePool buildCandidatePool(Integer tenantId, Integer storeId) {
        List<ShopStoreRider> riders = shopStoreRiderService.list(
            new LambdaQueryWrapper<ShopStoreRider>()
                .eq(ShopStoreRider::getTenantId, tenantId)
                .eq(ShopStoreRider::getIsDelete, 0)
                .eq(ShopStoreRider::getStatus, 1)
                .eq(ShopStoreRider::getWorkStatus, 1) // 仅在线
                .eq(ShopStoreRider::getAutoDispatchEnabled, 1)
                .eq(storeId != null, ShopStoreRider::getStoreId, storeId)
        );

        if (riders == null || riders.isEmpty()) {
            return new CandidatePool(List.of(), Map.of());
        }

        // 过滤无 userId/无坐标，并提前解析坐标（避免每单重复 parse）
        List<ShopStoreRider> candidates = riders.stream()
            .filter(Objects::nonNull)
            .filter(r -> r.getUserId() != null && r.getUserId() > 0)
            .filter(r -> parseLngLat(r.getLongitude(), r.getLatitude()) != null)
            .toList();

        if (candidates.isEmpty()) {
            return new CandidatePool(List.of(), Map.of());
        }

        List<Integer> riderUserIds = candidates.stream().map(ShopStoreRider::getUserId).distinct().toList();
        Map<Integer, Integer> onhand = loadOnhandCounts(tenantId, riderUserIds);
        return new CandidatePool(candidates, onhand);
    }

    private Map<Integer, Integer> loadOnhandCounts(Integer tenantId, List<Integer> riderUserIds) {
        if (riderUserIds == null || riderUserIds.isEmpty()) {
            return new HashMap<>();
        }

        // 统计各配送员“未完成”的在途单数：10/20/30
        QueryWrapper<GltTicketOrder> qw = new QueryWrapper<>();
        qw.select("rider_id AS riderId", "COUNT(1) AS cnt")
            .eq("tenant_id", tenantId)
            .eq("deleted", 0)
            .in("rider_id", riderUserIds)
            .in("delivery_status",
                GltTicketOrderService.DELIVERY_STATUS_WAITING,
                GltTicketOrderService.DELIVERY_STATUS_DELIVERING,
                GltTicketOrderService.DELIVERY_STATUS_WAIT_CONFIRM
            )
            .groupBy("rider_id");

        Map<Integer, Integer> map = new HashMap<>();
        List<Map<String, Object>> rows = gltTicketOrderService.listMaps(qw);
        if (rows == null || rows.isEmpty()) {
            return map;
        }
        for (Map<String, Object> row : rows) {
            if (row == null) {
                continue;
            }
            Object riderIdObj = row.get("riderId");
            Object cntObj = row.get("cnt");
            if (!(riderIdObj instanceof Number) || !(cntObj instanceof Number)) {
                continue;
            }
            map.put(((Number) riderIdObj).intValue(), ((Number) cntObj).intValue());
        }
        return map;
    }

    private ShopStoreRider pickNearest(CandidatePool pool, LngLat receiver) {
        // 优先距离；距离相同用 dispatchPriority 兜底（越大越优先）；再用 userId 稳定排序
        return pool.candidates.stream()
            .filter(r -> {
                Integer userId = r.getUserId();
                if (userId == null || userId <= 0) {
                    return false;
                }
                int max = r.getMaxOnhandOrders() == null ? 0 : r.getMaxOnhandOrders();
                if (max <= 0) {
                    return true; // 0表示不限制
                }
                int onhand = pool.onhandCount.getOrDefault(userId, 0);
                return onhand < max;
            })
            .min(Comparator
                .comparingDouble((ShopStoreRider r) -> distanceMeters(receiver, parseLngLat(r.getLongitude(), r.getLatitude())))
                .thenComparingInt((ShopStoreRider r) -> -(r.getDispatchPriority() == null ? 0 : r.getDispatchPriority()))
                .thenComparing(r -> r.getUserId() == null ? Integer.MAX_VALUE : r.getUserId())
            )
            .orElse(null);
    }

    private static double distanceMeters(LngLat a, LngLat b) {
        if (a == null || b == null) {
            return Double.POSITIVE_INFINITY;
        }
        // Haversine
        double lat1 = Math.toRadians(a.lat);
        double lat2 = Math.toRadians(b.lat);
        double dLat = lat2 - lat1;
        double dLng = Math.toRadians(b.lng - a.lng);
        double sinLat = Math.sin(dLat / 2);
        double sinLng = Math.sin(dLng / 2);
        double aa = sinLat * sinLat + Math.cos(lat1) * Math.cos(lat2) * sinLng * sinLng;
        double c = 2 * Math.atan2(Math.sqrt(aa), Math.sqrt(1 - aa));
        return 6371000.0 * c; // Earth radius (meters)
    }

    private LngLat resolveReceiverLngLat(Integer tenantId, GltTicketOrder order) {
        if (order == null) {
            return null;
        }

        // 1) 优先使用“商城订单快照坐标”（ShopOrder.addressLat/addressLng），避免地址表坐标被事后修改影响历史订单派单。
        LngLat byOrderSnapshot = resolveReceiverLngLatFromShopOrderSnapshot(tenantId, order);
        if (byOrderSnapshot != null) {
            return byOrderSnapshot;
        }

        // 2) 兜底：用地址表坐标（适用于没有关联商城订单的水票下单等场景）
        Integer addressId = order.getAddressId();
        if (addressId == null || addressId <= 0) {
            return null;
        }
        ShopUserAddress addr = shopUserAddressService.getOne(new LambdaQueryWrapper<ShopUserAddress>()
            .select(ShopUserAddress::getLat, ShopUserAddress::getLng)
            .eq(ShopUserAddress::getId, addressId)
            .eq(ShopUserAddress::getTenantId, tenantId)
            .last("limit 1"));
        return addr == null ? null : parseLngLat(addr.getLng(), addr.getLat());
    }

    private LngLat resolveReceiverLngLatFromShopOrderSnapshot(Integer tenantId, GltTicketOrder ticketOrder) {
        Integer userTicketId = ticketOrder.getUserTicketId();
        if (userTicketId == null || userTicketId <= 0) {
            return null;
        }

        GltUserTicket userTicket = gltUserTicketService.getOne(new LambdaQueryWrapper<GltUserTicket>()
            .select(GltUserTicket::getOrderId, GltUserTicket::getOrderNo, GltUserTicket::getOrderGoodsId)
            .eq(GltUserTicket::getTenantId, tenantId)
            .eq(GltUserTicket::getDeleted, 0)
            .eq(GltUserTicket::getId, userTicketId)
            .last("limit 1"));
        if (userTicket == null) {
            return null;
        }

        Integer shopOrderId = userTicket.getOrderId();
        String shopOrderNo = userTicket.getOrderNo();

        // 兼容：历史数据只写了 orderGoodsId
        if (shopOrderId == null && !StringUtils.hasText(shopOrderNo) && userTicket.getOrderGoodsId() != null) {
            ShopOrderGoods og = shopOrderGoodsService.getOne(new LambdaQueryWrapper<ShopOrderGoods>()
                .select(ShopOrderGoods::getOrderId)
                .eq(ShopOrderGoods::getTenantId, tenantId)
                .eq(ShopOrderGoods::getId, userTicket.getOrderGoodsId())
                .last("limit 1"));
            if (og != null) {
                shopOrderId = og.getOrderId();
            }
        }

        LambdaQueryWrapper<ShopOrder> qw = new LambdaQueryWrapper<ShopOrder>()
            .select(ShopOrder::getAddressLat, ShopOrder::getAddressLng)
            .eq(ShopOrder::getTenantId, tenantId)
            .eq(ShopOrder::getDeleted, 0)
            .last("limit 1");
        if (shopOrderId != null && shopOrderId > 0) {
            qw.eq(ShopOrder::getOrderId, shopOrderId);
        } else if (StringUtils.hasText(shopOrderNo)) {
            qw.eq(ShopOrder::getOrderNo, shopOrderNo);
        } else {
            return null;
        }

        ShopOrder shopOrder = shopOrderService.getOne(qw);
        if (shopOrder == null) {
            return null;
        }
        return parseLngLat(shopOrder.getAddressLng(), shopOrder.getAddressLat());
    }

    private static LngLat parseLngLat(String lngRaw, String latRaw) {
        Double lng = parseDoubleOrNull(lngRaw);
        Double lat = parseDoubleOrNull(latRaw);
        if (lng == null || lat == null) {
            return null;
        }

        // 若明显是 (lat,lng) 则交换
        if (Math.abs(lng) <= 90 && Math.abs(lat) > 90 && Math.abs(lat) <= 180) {
            double tmp = lng;
            lng = lat;
            lat = tmp;
        }
        if (Math.abs(lat) > 90 || Math.abs(lng) > 180) {
            return null;
        }
        return new LngLat(lng, lat);
    }

    private static Double parseDoubleOrNull(String raw) {
        if (raw == null) {
            return null;
        }
        String s = raw.trim();
        if (s.isEmpty()) {
            return null;
        }
        try {
            return Double.parseDouble(s);
        } catch (Exception e) {
            return null;
        }
    }

    private record LngLat(double lng, double lat) {
    }

    private static final class CandidatePool {
        final List<ShopStoreRider> candidates;
        final Map<Integer, Integer> onhandCount;

        CandidatePool(List<ShopStoreRider> candidates, Map<Integer, Integer> onhandCount) {
            this.candidates = candidates;
            this.onhandCount = new HashMap<>(onhandCount == null ? Map.of() : onhandCount);
        }
    }
}
