package com.zksy.base.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zksy.base.domain.EquipmentBase;
import com.zksy.base.domain.EquipmentPointRel;
import com.zksy.base.domain.MonitorPoint;
import com.zksy.base.mapper.EquipmentBaseMapper;
import com.zksy.base.mapper.EquipmentPointRelMapper;
import com.zksy.base.mapper.MonitorPointMapper;
import com.zksy.base.pressure.domain.FirefightingPressure;
import com.zksy.base.pressure.mapper.FirefightingPressureMapper;
import com.zksy.base.service.PressureMonitorService;
import com.zksy.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PressureMonitorServiceImpl implements PressureMonitorService {

    @Autowired
    private MonitorPointMapper monitorPointMapper;
    @Autowired
    private EquipmentPointRelMapper equipmentPointRelMapper;
    @Autowired
    private EquipmentBaseMapper equipmentBaseMapper;
    @Autowired
    private FirefightingPressureMapper firefightingPressureMapper;

    private static final DateTimeFormatter DT_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /** 压力等级 → 阈值映射 */
    private static final Map<String, double[]> PRESSURE_THRESHOLDS = new LinkedHashMap<>();
    static {
        PRESSURE_THRESHOLDS.put("high",   new double[]{240, 420});
        PRESSURE_THRESHOLDS.put("medium", new double[]{150, 250});
        PRESSURE_THRESHOLDS.put("low",    new double[]{80,  150});
    }

    @Override
    public List<Map<String, Object>> getPoints() {
        List<MonitorPoint> points = monitorPointMapper.selectList(
                new LambdaQueryWrapper<MonitorPoint>()
                        .eq(MonitorPoint::getPointType, "GAS")
                        .eq(MonitorPoint::getDelFlag, "0")
        );

        List<Map<String, Object>> result = new ArrayList<>();
        for (MonitorPoint p : points) {
            // 查关联的压力设备
            FirefightingPressure latestPressure = getLatestPressureByPoint(p.getPointId());
            if (latestPressure == null) continue; // 只返回有压力数据的监测点

            // 从设备信息推断压力等级
            String pressureLevel = inferPressureLevel(latestPressure);

            double[] thresholds = PRESSURE_THRESHOLDS.getOrDefault(pressureLevel, new double[]{150, 250});
            double minP = thresholds[0];
            double maxP = thresholds[1];
            double latestVal = latestPressure.getPressureValue() != null ? latestPressure.getPressureValue() : 0;
            String status = computeStatus(latestVal, minP, maxP);

            Map<String, Object> item = new LinkedHashMap<>();
            item.put("pointId", p.getPointId());
            item.put("name", p.getPointName());
            item.put("pipelineName", derivePipelineName(p.getPointName(), pressureLevel));
            item.put("pressureLevel", pressureLevel);
            item.put("address", p.getLocation());
            item.put("lng", p.getLongitude());
            item.put("lat", p.getLatitude());
            item.put("latestPressure", Math.round(latestVal * 10.0) / 10.0);
            item.put("minPressure", minP);
            item.put("maxPressure", maxP);
            item.put("status", status);
            item.put("latestTime", latestPressure.getObservedTime() != null
                    ? latestPressure.getObservedTime().format(DT_FMT) : "");
            result.add(item);
        }
        return result;
    }

    @Override
    public Map<String, Object> getTrend(String pointId, int days) {
        Map<String, Object> result = new LinkedHashMap<>();
        String telemCode = getTelemCodeByPoint(pointId);

        // 先查所有数据确定时间范围
        List<FirefightingPressure> allData = firefightingPressureMapper.selectList(
                new LambdaQueryWrapper<FirefightingPressure>()
                        .eq(StringUtils.isNotEmpty(telemCode), FirefightingPressure::getTelemeteringStation, telemCode)
                        .orderByAsc(FirefightingPressure::getObservedTime)
        );

        // 从数据中提取时间范围
        LocalDateTime dataStart = allData.isEmpty() ? LocalDateTime.now().withHour(0).withMinute(0).withSecond(0).withNano(0)
                : allData.get(0).getObservedTime();
        LocalDateTime dataEnd = allData.isEmpty() ? dataStart.plusDays(9).withHour(23)
                : allData.get(allData.size() - 1).getObservedTime();
        if (dataEnd == null) dataEnd = dataStart.plusDays(9).withHour(23);

        int actualDays = Math.max(1, (int) java.time.Duration.between(dataStart, dataEnd).toDays() + 1);
        actualDays = Math.min(actualDays, days);

        // 聚合为每天6时段 (0/4/8/12/16/20)
        int[] hourSlots = {0, 4, 8, 12, 16, 20};
        Map<String, List<Double>> grouped = new LinkedHashMap<>();
        for (FirefightingPressure fp : allData) {
            if (fp.getObservedTime() == null || fp.getPressureValue() == null) continue;
            int slotHour = findNearestSlot(fp.getObservedTime().getHour(), hourSlots);
            String key = fp.getObservedTime().toLocalDate().toString() + " " + String.format("%02d:00", slotHour);
            grouped.computeIfAbsent(key, k -> new ArrayList<>()).add(fp.getPressureValue());
        }

        LocalDateTime rangeStart = dataStart.withHour(0).withMinute(0).withSecond(0).withNano(0);
        LocalDateTime rangeEnd = dataEnd.withHour(23).withMinute(0).withSecond(0).withNano(0);

        List<String> times = new ArrayList<>();
        List<Double> pressures = new ArrayList<>();
        double lastVal = 0;

        for (int d = actualDays - 1; d >= 0; d--) {
            LocalDateTime day = rangeEnd.toLocalDate().atStartOfDay().minusDays(d);
            for (int h : hourSlots) {
                String key = day.toLocalDate().toString() + " " + String.format("%02d:00", h);
                List<Double> vals = grouped.getOrDefault(key, Collections.emptyList());
                double avg = vals.isEmpty() ? lastVal : vals.stream().mapToDouble(v -> v).average().orElse(lastVal);
                if (!vals.isEmpty()) lastVal = avg;
                times.add(day.toLocalDate().toString().substring(5) + "\n" + String.format("%02d:00", h));
                pressures.add(Math.round(avg * 10.0) / 10.0);
            }
        }

        // 从数据推断阈值
        double minP = 240, maxP = 420;
        FirefightingPressure latest = allData.isEmpty() ? null : allData.get(allData.size() - 1);
        if (latest != null) {
            double[] thresholds = inferThresholds(latest);
            minP = thresholds[0];
            maxP = thresholds[1];
        }

        result.put("times", times);
        result.put("pressures", pressures);
        result.put("minPressure", minP);
        result.put("maxPressure", maxP);
        return result;
    }

    @Override
    public Map<String, Object> getHistory(String pointId, int pageNum, int pageSize) {
        String telemCode = getTelemCodeByPoint(pointId);

        // 总数
        long total = firefightingPressureMapper.selectCount(
                new LambdaQueryWrapper<FirefightingPressure>()
                        .eq(StringUtils.isNotEmpty(telemCode), FirefightingPressure::getTelemeteringStation, telemCode)
        );

        int offset = (pageNum - 1) * pageSize;
        List<FirefightingPressure> list = firefightingPressureMapper.selectList(
                new LambdaQueryWrapper<FirefightingPressure>()
                        .eq(StringUtils.isNotEmpty(telemCode), FirefightingPressure::getTelemeteringStation, telemCode)
                        .orderByDesc(FirefightingPressure::getObservedTime)
                        .last("limit " + offset + ", " + pageSize)
        );

        List<Map<String, Object>> rows = new ArrayList<>();
        for (FirefightingPressure fp : list) {
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("time", fp.getObservedTime() != null ? fp.getObservedTime().format(DT_FMT) : "");
            row.put("pressure", fp.getPressureValue());
            row.put("collector", "YLCG-" + (telemCode != null ? telemCode.substring(telemCode.length() - 3) : "001"));
            row.put("remark", "");
            rows.add(row);
        }

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("total", total);
        result.put("rows", rows);
        return result;
    }

    // ========== 辅助方法 ==========

    private String getTelemCodeByPoint(String pointId) {
        List<EquipmentPointRel> rels = equipmentPointRelMapper.selectList(
                new LambdaQueryWrapper<EquipmentPointRel>()
                        .eq(EquipmentPointRel::getPointId, pointId)
                        .eq(EquipmentPointRel::getDelFlag, "0")
        );
        if (rels.isEmpty()) return null;
        List<EquipmentBase> eqs = equipmentBaseMapper.selectBatchIds(
                rels.stream().map(EquipmentPointRel::getEquipmentId).collect(Collectors.toList()));
        for (EquipmentBase eq : eqs) {
            // 压力设备的 equipment_code 即 telemetering_station
            FirefightingPressure check = getLatestByTelemCode(eq.getEquipmentCode());
            if (check != null) return eq.getEquipmentCode();
        }
        return null;
    }

    private FirefightingPressure getLatestPressureByPoint(String pointId) {
        String telemCode = getTelemCodeByPoint(pointId);
        if (telemCode == null) return null;
        return getLatestByTelemCode(telemCode);
    }

    private FirefightingPressure getLatestByTelemCode(String telemCode) {
        List<FirefightingPressure> list = firefightingPressureMapper.selectList(
                new LambdaQueryWrapper<FirefightingPressure>()
                        .eq(FirefightingPressure::getTelemeteringStation, telemCode)
                        .orderByDesc(FirefightingPressure::getObservedTime)
                        .last("limit 1")
        );
        return list.isEmpty() ? null : list.get(0);
    }

    private String inferPressureLevel(FirefightingPressure fp) {
        if (fp == null || fp.getPressureValue() == null) return "medium";
        double v = fp.getPressureValue();
        if (v >= 240) return "high";
        if (v >= 150) return "medium";
        return "low";
    }

    private double[] inferThresholds(FirefightingPressure fp) {
        return PRESSURE_THRESHOLDS.getOrDefault(inferPressureLevel(fp), new double[]{150, 250});
    }

    private String computeStatus(double val, double min, double max) {
        if (val <= 0) return "offline";
        if (val > max) return "danger";
        if (val < min) return "warning";
        return "normal";
    }

    private String derivePipelineName(String pointName, String level) {
        String levelStr = "高压".equals(level) ? "高压段" : "中压".equals(level) ? "中压段" : "低压段";
        return pointName.replace("监测点", "").replace("LNG储配站", "LNG段").replace("计量站", "计量段") + levelStr;
    }

    private int findNearestSlot(int hour, int[] slots) {
        int nearest = slots[0];
        int minDiff = Math.abs(hour - slots[0]);
        for (int s : slots) {
            int diff = Math.abs(hour - s);
            if (diff < minDiff) { minDiff = diff; nearest = s; }
        }
        return nearest;
    }
}
