package com.ai.trackwalker.model;

import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

@Component
public class ModelParameterValidator {
    private static final Set<String> PRECISIONS = Set.of("FP32", "FP16", "INT8");

    public ValidationResult validate(String modelType, Map<String, Object> input) {
        Map<String, Object> parameters = new LinkedHashMap<>(input == null ? Map.of() : input);
        List<ValidationMessage> errors = new ArrayList<>();
        List<ValidationMessage> warnings = new ArrayList<>();

        switch (modelType) {
            case "detection" -> validateDetection(parameters, errors, warnings);
            case "segmentation" -> validateSegmentation(parameters, errors, warnings);
            case "thermal" -> validateThermal(parameters, errors, warnings);
            case "pointcloud" -> validatePointCloud(parameters, errors, warnings);
            case "change" -> validateChange(parameters, errors, warnings);
            default -> errors.add(message("model_type", "unsupported_model_type", "不支持的模型类型"));
        }

        String precision = String.valueOf(parameters.getOrDefault("precision", "FP16"));
        if (parameters.containsKey("precision") && !PRECISIONS.contains(precision)) {
            errors.add(message("precision", "invalid_enum", "计算精度不在允许范围内"));
        }
        double memoryGb = estimateMemory(modelType, parameters);
        Map<String, Object> estimate = Map.of(
                "memory_gb", round(memoryGb, 2),
                "relative_latency", round(memoryGb / 8.0, 2),
                "device_pool", memoryGb > 12 ? "gpu-large" : "gpu-standard"
        );
        return new ValidationResult(errors.isEmpty(), parameters, errors, warnings, estimate);
    }

    private void validateDetection(Map<String, Object> p, List<ValidationMessage> errors, List<ValidationMessage> warnings) {
        range(p, "confidence_threshold", 0.05, 0.99, errors);
        range(p, "nms_iou_threshold", 0.1, 0.9, errors);
        integerRange(p, "max_detections", 1, 500, errors);
        integerAllowed(p, "input_size", Set.of(640, 960, 1280), errors);
        if (Boolean.TRUE.equals(p.get("enable_tiling"))) {
            range(p, "tile_overlap", 0.05, 0.5, errors);
        }
        if (number(p, "confidence_threshold", 0.55) > 0.85) {
            warnings.add(message("confidence_threshold", "recall_risk", "置信度阈值较高，可能降低长尾类别召回率"));
        }
    }

    private void validateSegmentation(Map<String, Object> p, List<ValidationMessage> errors, List<ValidationMessage> warnings) {
        range(p, "mask_threshold", 0.05, 0.95, errors);
        integerRange(p, "minimum_area", 4, 10000, errors);
        range(p, "contour_simplification", 0, 5, errors);
        integerAllowed(p, "input_size", Set.of(640, 1024, 1280), errors);
        if (number(p, "minimum_area", 64) > 2000) {
            warnings.add(message("minimum_area", "small_region_risk", "最小区域面积较大，细小裂缝可能被过滤"));
        }
    }

    private void validateThermal(Map<String, Object> p, List<ValidationMessage> errors, List<ValidationMessage> warnings) {
        range(p, "emissivity", 0.1, 1, errors);
        range(p, "ambient_temperature_c", -50, 80, errors);
        range(p, "reflected_temperature_c", -50, 100, errors);
        range(p, "distance_m", 0.5, 500, errors);
        integerRange(p, "humidity_percent", 0, 100, errors);
        range(p, "hotspot_threshold_c", 20, 200, errors);
        range(p, "relative_rise_threshold_c", 1, 100, errors);
        if (number(p, "emissivity", 0.95) < 0.7) {
            warnings.add(message("emissivity", "calibration_required", "低发射率材料需要使用现场标定值"));
        }
    }

    private void validatePointCloud(Map<String, Object> p, List<ValidationMessage> errors, List<ValidationMessage> warnings) {
        range(p, "voxel_size_m", 0.01, 1, errors);
        integerRange(p, "outlier_neighbors", 4, 100, errors);
        range(p, "outlier_std_ratio", 0.5, 5, errors);
        range(p, "icp_max_distance_m", 0.05, 5, errors);
        integerRange(p, "icp_iterations", 10, 500, errors);
        range(p, "registration_error_limit_m", 0.01, 1, errors);
        if (number(p, "icp_max_distance_m", 0.5) <= number(p, "voxel_size_m", 0.08)) {
            errors.add(message("icp_max_distance_m", "cross_field", "ICP 最大匹配距离必须大于体素尺寸"));
        }
        if (number(p, "voxel_size_m", 0.08) < 0.03) {
            warnings.add(message("voxel_size_m", "resource_cost", "体素尺寸较小，将显著增加显存和处理时间"));
        }
    }

    private void validateChange(Map<String, Object> p, List<ValidationMessage> errors, List<ValidationMessage> warnings) {
        range(p, "target_resolution_m", 0.01, 2, errors);
        range(p, "registration_precision_m", 0.01, 2, errors);
        range(p, "change_threshold", 0.01, 1, errors);
        range(p, "minimum_area_m2", 0.1, 1000, errors);
        integerAllowed(p, "morphology_kernel", Set.of(1, 3, 5, 7, 9), errors);
        if (number(p, "registration_precision_m", 0.2) > number(p, "target_resolution_m", 0.1) * 3) {
            warnings.add(message("registration_precision_m", "alignment_risk", "配准精度低于目标分辨率要求，可能产生伪变化区域"));
        }
    }

    private void range(Map<String, Object> p, String field, double min, double max, List<ValidationMessage> errors) {
        if (!p.containsKey(field)) {
            errors.add(message(field, "required", "缺少必填参数"));
            return;
        }
        double value = number(p, field, Double.NaN);
        if (Double.isNaN(value) || value < min || value > max) {
            errors.add(message(field, "out_of_range", "参数必须位于 " + min + " 至 " + max + " 之间"));
        }
    }

    private void integerRange(Map<String, Object> p, String field, int min, int max, List<ValidationMessage> errors) {
        if (!p.containsKey(field)) {
            errors.add(message(field, "required", "缺少必填参数"));
            return;
        }
        double value = number(p, field, Double.NaN);
        if (Double.isNaN(value) || value % 1 != 0 || value < min || value > max) {
            errors.add(message(field, "out_of_range", "参数必须是 " + min + " 至 " + max + " 之间的整数"));
        }
    }

    private void integerAllowed(Map<String, Object> p, String field, Set<Integer> allowed, List<ValidationMessage> errors) {
        if (!p.containsKey(field)) {
            errors.add(message(field, "required", "缺少必填参数"));
            return;
        }
        int value = (int) number(p, field, Integer.MIN_VALUE);
        if (!allowed.contains(value)) {
            errors.add(message(field, "invalid_enum", "参数值不在允许集合中"));
        }
    }

    private double estimateMemory(String modelType, Map<String, Object> p) {
        double base = switch (modelType) {
            case "pointcloud" -> 12;
            case "change" -> 9;
            case "segmentation" -> 7;
            case "thermal" -> 4;
            default -> 5;
        };
        double sizeFactor = number(p, "input_size", 960) / 960.0;
        double batchFactor = Math.max(1, number(p, "batch_size", 1));
        double precisionFactor = "FP32".equals(String.valueOf(p.get("precision"))) ? 1.45 : "INT8".equals(String.valueOf(p.get("precision"))) ? 0.7 : 1;
        return base * sizeFactor * Math.sqrt(batchFactor) * precisionFactor;
    }

    private double number(Map<String, Object> p, String field, double fallback) {
        Object value = p.get(field);
        if (value instanceof Number number) {
            return number.doubleValue();
        }
        try {
            return value == null ? fallback : Double.parseDouble(String.valueOf(value));
        } catch (NumberFormatException ignored) {
            return fallback;
        }
    }

    private double round(double value, int scale) {
        return BigDecimal.valueOf(value).setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    private ValidationMessage message(String field, String code, String message) {
        return new ValidationMessage(field, code, message);
    }

    public record ValidationMessage(String field, String code, String message) {
    }

    public record ValidationResult(
            boolean valid,
            Map<String, Object> normalizedParameters,
            List<ValidationMessage> errors,
            List<ValidationMessage> warnings,
            Map<String, Object> resourceEstimate
    ) {
    }
}
