package com.qaup.collision.rule.service.impl;

import com.qaup.collision.common.model.MovingObject;
import com.qaup.collision.common.model.MovingObject.MovingObjectType;
import com.qaup.collision.common.model.spatial.VehicleLocation;
import com.qaup.collision.common.model.spatial.AirportArea;
import com.qaup.collision.common.service.AirportAreaService;
import com.qaup.collision.road.service.RoadNetworkService;
import com.qaup.collision.road.model.RoadInfo;
import com.qaup.collision.rule.model.entity.SpatialRule;
import com.qaup.collision.rule.model.enums.RuleCategory;
import com.qaup.collision.rule.model.enums.RuleStatus;
import com.qaup.collision.rule.repository.SpatialRuleRepository;
import com.qaup.collision.rule.service.LocationRuleQueryService;
import com.qaup.collision.rule.service.RulePriorityService;
import com.qaup.collision.rule.service.SpatialRuleService;
import com.qaup.collision.rule.service.TimeWindowMatchingService;
import com.qaup.collision.rule.service.VehicleTypePermissionService;
import com.qaup.common.constant.GeoConstants;

import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.GeometryFactory;
import org.locationtech.jts.geom.Point;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 基于位置的适用规则查询服务实现类
 * 提供高效的空间规则查询和匹配功能
 * 
 * @author AI Assistant
 * @version 1.0
 * @since 2025-01-17
 */
@Service
@Transactional(readOnly = true)
public class LocationRuleQueryServiceImpl implements LocationRuleQueryService {

    private static final Logger logger = LoggerFactory.getLogger(LocationRuleQueryServiceImpl.class);

    @Autowired
    private SpatialRuleRepository spatialRuleRepository;

    @Autowired
    private SpatialRuleService spatialRuleService;

    @Autowired
    private TimeWindowMatchingService timeWindowMatchingService;

    @Autowired
    private VehicleTypePermissionService vehicleTypePermissionService;

    @Autowired
    private RulePriorityService rulePriorityService;

    @Autowired
    private AirportAreaService airportAreaService;

    @Autowired
    private RoadNetworkService roadNetworkService;

    private final GeometryFactory geometryFactory = new GeometryFactory();

    // ============================================
    // 核心查询方法
    // ============================================

    @Override
    @Cacheable(value = "locationRules", key = "#location.toString() + '_' + #vehicleType + '_' + #timestamp.toString()")
    public List<SpatialRule> findApplicableRules(Point location, MovingObjectType vehicleType, LocalDateTime timestamp) {
        try {
            logger.debug("查询位置适用规则: location={}, vehicleType={}, timestamp={}", 
                        location, vehicleType, timestamp);

            // 1. 查询所有活跃规则
            List<SpatialRule> activeRules = spatialRuleRepository.findByStatus(RuleStatus.ACTIVE);
            logger.debug("查询到活跃规则数量: {}", activeRules.size());

            // 2. 过滤空间范围内的规则
            List<SpatialRule> spatiallyApplicableRules = activeRules.stream()
                    .filter(rule -> {
                        boolean inScope = isLocationInRuleScope(location, rule);
                        logger.debug("规则空间检查: ruleId={}, spatialObjectType={}, spatialObjectId={}, inScope={}", 
                                   rule.getRuleId(), rule.getSpatialObjectType(), rule.getSpatialObjectId(), inScope);
                        return inScope;
                    })
                    .collect(Collectors.toList());
            logger.debug("空间范围内规则数量: {}", spatiallyApplicableRules.size());

            // 3. 过滤时间有效的规则
            List<SpatialRule> timeApplicableRules = spatiallyApplicableRules.stream()
                    .filter(rule -> {
                        boolean timeValid = isRuleEffectiveAtTime(rule, timestamp);
                        logger.debug("规则时间检查: ruleId={}, timeValid={}", rule.getRuleId(), timeValid);
                        return timeValid;
                    })
                    .collect(Collectors.toList());
            logger.debug("时间有效规则数量: {}", timeApplicableRules.size());

            // 4. 过滤车辆类型适用的规则（使用专门的权限检查服务）
            List<SpatialRule> applicableRules = timeApplicableRules.stream()
                    .filter(rule -> {
                        boolean typeApplicable = vehicleTypePermissionService.isVehicleTypeApplicableToRule(rule, vehicleType);
                        logger.debug("规则车辆类型检查: ruleId={}, vehicleType={}, typeApplicable={}", 
                                   rule.getRuleId(), vehicleType, typeApplicable);
                        return typeApplicable;
                    })
                    .collect(Collectors.toList());

            // 5. 按优先级排序（使用专门的优先级服务）
            applicableRules = rulePriorityService.sortRulesByPriority(applicableRules, vehicleType, timestamp);

            logger.debug("找到适用规则数量: {}", applicableRules.size());
            return applicableRules;

        } catch (Exception e) {
            logger.error("查询位置适用规则失败: location={}, vehicleType={}", location, vehicleType, e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<SpatialRule> findApplicableRulesByCategory(Point location, MovingObjectType vehicleType, 
                                                          RuleCategory ruleCategory, LocalDateTime timestamp) {
        try {
            List<SpatialRule> allApplicableRules = findApplicableRules(location, vehicleType, timestamp);
            
            return allApplicableRules.stream()
                    .filter(rule -> rule.getCategory() == ruleCategory)
                    .collect(Collectors.toList());

        } catch (Exception e) {
            logger.error("查询分类规则失败: location={}, category={}", location, ruleCategory, e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<SpatialRule> findApplicableRulesForVehicle(VehicleLocation vehicleLocation) {
        return findApplicableRules(
            vehicleLocation.getLocation(),
            vehicleLocation.getVehicleType(),
            vehicleLocation.getTimestamp()
        );
    }

    @Override
    public List<SpatialRule> findRulesInRadius(Point centerLocation, double radiusMeters, 
                                              MovingObjectType vehicleType, LocalDateTime timestamp) {
        try {
            // 使用PostGIS空间查询
            List<SpatialRule> rulesInRadius = spatialRuleService.findRulesWithinRadius(centerLocation, radiusMeters);
            
            // 过滤时间和车辆类型，并排序（使用专门的权限检查和优先级服务）
            List<SpatialRule> filteredRules = rulesInRadius.stream()
                    .filter(rule -> isRuleEffectiveAtTime(rule, timestamp))
                    .filter(rule -> vehicleTypePermissionService.isVehicleTypeApplicableToRule(rule, vehicleType))
                    .collect(Collectors.toList());
            
            return rulePriorityService.sortRulesByPriority(filteredRules, vehicleType, timestamp);

        } catch (Exception e) {
            logger.error("查询半径内规则失败: center={}, radius={}", centerLocation, radiusMeters, e);
            return new ArrayList<>();
        }
    }

    @Override
    public List<SpatialRule> findRulesAlongTrajectory(List<Point> trajectory, MovingObjectType vehicleType, 
                                                     LocalDateTime timestamp) {
        try {
            Set<SpatialRule> rulesOnTrajectory = new HashSet<>();
            
            for (Point point : trajectory) {
                List<SpatialRule> rulesAtPoint = findApplicableRules(point, vehicleType, timestamp);
                rulesOnTrajectory.addAll(rulesAtPoint);
            }
            
            List<SpatialRule> rulesList = new ArrayList<>(rulesOnTrajectory);
            return rulePriorityService.sortRulesByPriority(rulesList, vehicleType, timestamp);

        } catch (Exception e) {
            logger.error("查询轨迹规则失败: trajectorySize={}", trajectory.size(), e);
            return new ArrayList<>();
        }
    }

    // ============================================
    // 规则适用性检查
    // ============================================

    @Override
    public boolean isLocationInRuleScope(Point location, SpatialRule rule) {
        try {
            Geometry ruleGeometry = getRuleBoundary(rule);
            if (ruleGeometry == null) {
                return false;
            }
            
            return ruleGeometry.contains(location);

        } catch (Exception e) {
            logger.warn("检查位置规则范围失败: ruleId={}", rule.getRuleId(), e);
            return false;
        }
    }

    @Override
    public boolean isRuleApplicableToVehicleType(SpatialRule rule, MovingObjectType vehicleType) {
        // 委托给专门的车辆类型权限检查服务
        return vehicleTypePermissionService.isVehicleTypeApplicableToRule(rule, vehicleType);
    }

    @Override
    public boolean isRuleEffectiveAtTime(SpatialRule rule, LocalDateTime timestamp) {
        try {
            // 使用TimeWindowMatchingService进行统一的时间有效性检查
            return timeWindowMatchingService.isRuleEffectiveAtTime(rule, timestamp);

        } catch (Exception e) {
            logger.error("检查规则时间有效性失败: ruleId={}, timestamp={}", rule.getRuleId(), timestamp, e);
            return false;
        }
    }

    // ============================================
    // 统计和分析方法
    // ============================================

    @Override
    public RuleCoverageStats getRuleCoverageStats(Point location, MovingObjectType vehicleType, LocalDateTime timestamp) {
        try {
            List<SpatialRule> allRules = spatialRuleRepository.findAll();
            List<SpatialRule> applicableRules = findApplicableRules(location, vehicleType, timestamp);
            
            // 按分类统计
            Map<RuleCategory, Integer> rulesByCategory = applicableRules.stream()
                    .collect(Collectors.groupingBy(
                        SpatialRule::getCategory,
                        Collectors.collectingAndThen(Collectors.counting(), Math::toIntExact)
                    ));
            
            // 按状态统计
            int activeRules = (int) applicableRules.stream()
                    .filter(rule -> rule.getStatus() == RuleStatus.ACTIVE)
                    .count();
            
            int inactiveRules = applicableRules.size() - activeRules;
            
            return new RuleCoverageStats(
                allRules.size(),
                applicableRules.size(),
                rulesByCategory,
                activeRules,
                inactiveRules
            );

        } catch (Exception e) {
            logger.error("获取规则覆盖统计失败: location={}", location, e);
            return new RuleCoverageStats();
        }
    }

    @Override
    public Map<Point, List<SpatialRule>> findApplicableRulesForLocations(List<Point> locations, 
                                                                        MovingObjectType vehicleType, 
                                                                        LocalDateTime timestamp) {
        Map<Point, List<SpatialRule>> result = new HashMap<>();
        
        for (Point location : locations) {
            List<SpatialRule> rules = findApplicableRules(location, vehicleType, timestamp);
            result.put(location, rules);
        }
        
        return result;
    }

    @Override
    public SpatialRule findHighestPriorityRule(Point location, MovingObjectType vehicleType, 
                                              RuleCategory ruleCategory, LocalDateTime timestamp) {
        try {
            List<SpatialRule> applicableRules = findApplicableRulesByCategory(location, vehicleType, ruleCategory, timestamp);
            
            return applicableRules.stream()
                    .min(Comparator.comparing(SpatialRule::getPriority))
                    .orElse(null);

        } catch (Exception e) {
            logger.error("查询最高优先级规则失败: location={}, category={}", location, ruleCategory, e);
            return null;
        }
    }

    // ============================================
    // 预测和路径分析
    // ============================================

    @Override
    public List<SpatialRule> predictRulesOnPath(Point currentLocation, double heading, double speed, 
                                               int predictionTimeSeconds, MovingObjectType vehicleType) {
        try {
            // 计算预测路径上的点
            List<Point> predictedPath = calculatePredictedPath(currentLocation, heading, speed, predictionTimeSeconds);
            
            // 查询路径上的规则
            LocalDateTime futureTime = LocalDateTime.now().plusSeconds(predictionTimeSeconds);
            return findRulesAlongTrajectory(predictedPath, vehicleType, futureTime);

        } catch (Exception e) {
            logger.error("预测路径规则失败: location={}, heading={}, speed={}", currentLocation, heading, speed, e);
            return new ArrayList<>();
        }
    }

    @Override
    public Geometry getRuleBoundary(SpatialRule rule) {
        try {
            // 根据空间对象类型获取几何形状
            switch (rule.getSpatialObjectType()) {
                case AREA:
                    if (rule.getSpatialObjectId() != null) {
                        // 从区域获取几何形状
                        return getGeometryFromAreaCheck(rule.getSpatialObjectId());
                    }
                    break;
                case ROAD:
                    if (rule.getSpatialObjectId() != null) {
                        return getGeometryFromRoadCheck(rule.getSpatialObjectId());
                    }
                    break;
                case INTERSECTION:
                    if (rule.getSpatialObjectId() != null) {
                        return getGeometryFromIntersectionCheck(rule.getSpatialObjectId());
                    }
                    break;
                case CUSTOM_GEOMETRY:
                    return rule.getCustomGeometry();
                default:
                    return null;
            }
            
            return null;

        } catch (Exception e) {
            logger.error("获取规则边界失败: ruleId={}", rule.getRuleId(), e);
            return null;
        }
    }

    @Override
    public double calculateDistanceToRule(Point location, SpatialRule rule) {
        try {
            Geometry ruleGeometry = getRuleBoundary(rule);
            if (ruleGeometry == null) {
                return Double.MAX_VALUE;
            }
            
            if (ruleGeometry.contains(location)) {
                return 0.0;
            }
            
            return ruleGeometry.distance(location) * GeoConstants.DEGREE_TO_METER; // 转换为米（近似）

        } catch (Exception e) {
            logger.error("计算到规则距离失败: ruleId={}", rule.getRuleId(), e);
            return Double.MAX_VALUE;
        }
    }

    @Override
    public List<SpatialRule> findUpcomingRules(Point location, MovingObjectType vehicleType, int lookAheadMinutes) {
        try {
            LocalDateTime currentTime = LocalDateTime.now();
            LocalDateTime futureTime = currentTime.plusMinutes(lookAheadMinutes);
            
            // 查询当前不适用但将来会适用的规则
            List<SpatialRule> currentRules = findApplicableRules(location, vehicleType, currentTime);
            List<SpatialRule> futureRules = findApplicableRules(location, vehicleType, futureTime);
            
            // 找出差异（将来适用但现在不适用的规则）
            Set<String> currentRuleIds = currentRules.stream()
                    .map(SpatialRule::getRuleId)
                    .collect(Collectors.toSet());
            
            return futureRules.stream()
                    .filter(rule -> !currentRuleIds.contains(rule.getRuleId()))
                    .collect(Collectors.toList());

        } catch (Exception e) {
            logger.error("查询即将生效规则失败: location={}, lookAheadMinutes={}", location, lookAheadMinutes, e);
            return new ArrayList<>();
        }
    }

    // ============================================
    // 私有辅助方法
    // ============================================

    private List<Point> calculatePredictedPath(Point currentLocation, double heading, double speed, int predictionTimeSeconds) {
        List<Point> path = new ArrayList<>();
        
        // 简化实现：每秒计算一个点
        double headingRadians = Math.toRadians(heading);
        double deltaX = speed * Math.cos(headingRadians);
        double deltaY = speed * Math.sin(headingRadians);
        
        // 转换为经纬度增量（近似）
        double deltaLon = deltaX / 111320.0; // 1度经度约111320米
        double deltaLat = deltaY / 110540.0; // 1度纬度约110540米
        
        for (int i = 0; i <= predictionTimeSeconds; i++) {
            double newLon = currentLocation.getX() + deltaLon * i;
            double newLat = currentLocation.getY() + deltaLat * i;
            
            Point predictedPoint = geometryFactory.createPoint(new Coordinate(newLon, newLat));
            path.add(predictedPoint);
        }
        
        return path;
    }

    /**
     * 检查复杂时间模式匹配
     * @param rule 规则
     * @param timestamp 时间戳
     * @return 如果匹配返回true
     */
    private boolean matchesComplexTimePatterns(SpatialRule rule, LocalDateTime timestamp) {
        // 使用TimeWindowMatchingService处理复杂时间模式匹配
        return timeWindowMatchingService.matchesComplexTimePatterns(rule.getTimePatterns(), timestamp);
    }

    /**
     * 从区域检查获取几何形状
     * @param areaId 区域ID
     * @return 区域几何形状，如果无法获取返回null
     */
    private Geometry getGeometryFromAreaCheck(String areaId) {
        try {
            logger.debug("从区域ID获取几何形状: areaId={}", areaId);
            
            // 使用AirportAreaService根据区域ID获取区域信息
            Optional<AirportArea> areaOpt = airportAreaService.getAreaByAreaId(areaId);
            
            if (areaOpt.isPresent()) {
                AirportArea area = areaOpt.get();
                Geometry boundary = area.getBoundary();
                
                if (boundary != null) {
                    logger.debug("成功获取区域几何形状: areaId={}, geometryType={}", 
                               areaId, boundary.getGeometryType());
                    return boundary;
                } else {
                    logger.warn("区域几何形状为空: areaId={}", areaId);
                }
            } else {
                logger.warn("未找到区域: areaId={}", areaId);
            }
            
            return null;
            
        } catch (Exception e) {
            logger.error("从区域ID获取几何形状失败: areaId={}", areaId, e);
            return null;
        }
    }

    /**
     * 从道路检查获取几何形状
     * @param roadId 道路ID
     * @return 道路几何形状，如果无法获取返回null
     */
    private Geometry getGeometryFromRoadCheck(String roadId) {
        try {
            logger.debug("从道路ID获取几何形状: roadId={}", roadId);
            
            // 使用RoadNetworkService根据道路ID获取道路信息
            Optional<RoadInfo> roadOpt = roadNetworkService.getRoadById(roadId);
            
            if (roadOpt.isPresent()) {
                RoadInfo road = roadOpt.get();
                Geometry boundary = road.getBoundary();
                
                if (boundary != null) {
                    logger.debug("成功获取道路几何形状: roadId={}, geometryType={}", 
                               roadId, boundary.getGeometryType());
                    return boundary;
                } else {
                    logger.warn("道路几何形状为空: roadId={}", roadId);
                }
            } else {
                logger.warn("未找到道路: roadId={}", roadId);
            }
            
            return null;
            
        } catch (Exception e) {
            logger.error("从道路ID获取几何形状失败: roadId={}", roadId, e);
            return null;
        }
    }

    /**
     * 从路口检查获取几何形状
     * @param intersectionId 路口ID
     * @return 路口几何形状，如果无法获取返回null
     */
    private Geometry getGeometryFromIntersectionCheck(String intersectionId) {
        // TODO: 从intersections表或相关服务获取路口几何形状
        logger.debug("从路口ID获取几何形状功能待实现: intersectionId={}", intersectionId);
        return null; // 暂时返回null，后续集成路口服务
    }

    @Override
    public boolean hasAccessPermission(Point location, MovingObjectType vehicleType, LocalDateTime timestamp) {
        List<SpatialRule> applicableRules = findApplicableRules(location, vehicleType, timestamp);
        List<SpatialRule> accessControlRules = applicableRules.stream()
                .filter(rule -> rule.getCategory() == RuleCategory.ACCESS_CONTROL)
                .collect(Collectors.toList());
        if (accessControlRules.isEmpty()) {
            return true;
        }
        boolean hasProhibition = accessControlRules.stream()
                .anyMatch(rule -> !vehicleTypePermissionService.isVehicleTypeApplicableToRule(rule, vehicleType));
        return !hasProhibition;
    }

    @Override
    public List<SpatialRule> getRestrictedRules(Point location, MovingObjectType vehicleType, LocalDateTime timestamp) {
        List<SpatialRule> allRules = findApplicableRules(location, vehicleType, timestamp);
        return allRules.stream()
                .filter(rule -> !vehicleTypePermissionService.isVehicleTypeApplicableToRule(rule, vehicleType))
                .collect(Collectors.toList());
    }

    @Override
    public List<SpatialRule> getAllowedRules(Point location, MovingObjectType vehicleType, LocalDateTime timestamp) {
        List<SpatialRule> allRules = findApplicableRules(location, vehicleType, timestamp);
        return allRules.stream()
                .filter(rule -> vehicleTypePermissionService.isVehicleTypeApplicableToRule(rule, vehicleType))
                .collect(Collectors.toList());
    }
} 