package vip.xiaonuo.coldchain.core.service;

import com.github.jfcloud.influxdb.config.JfcloudInfluxDB2Properties;
import com.github.jfcloud.influxdb.flux.AggregationWindow;
import com.github.jfcloud.influxdb.flux.JfcloudFluxDataService;
import com.github.jfcloud.influxdb.service.JfcloudInfluxDBService;
import com.github.jfcloud.influxdb.util.TimeUtils;
import com.influxdb.client.QueryApi;
import com.influxdb.query.FluxTable;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import vip.xiaonuo.coldchain.core.alarm.enums.SensorAlarmType;
import vip.xiaonuo.coldchain.core.alarm.service.SensorAlarmService;
import vip.xiaonuo.coldchain.core.bean.influxdb.SensorData;
import vip.xiaonuo.coldchain.core.config.JfcloudColdChainConstants;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author jackzhou
 * @version 1.0
 * @project jfcloud-coldchain
 * @description
 * @date 2024/11/25 12:35:02
 */
@Service
public class JfcloudSensorDataService extends JfcloudFluxDataService<SensorData> {
    private SensorAlarmService sensorAlarmService;

    public JfcloudSensorDataService(JfcloudInfluxDBService jfcloudInfluxDBService, JfcloudInfluxDB2Properties jfcloudInfluxDB2Properties, SensorAlarmService sensorAlarmService) {
        super(jfcloudInfluxDBService, jfcloudInfluxDB2Properties);
        this.sensorAlarmService = sensorAlarmService;
    }


    /**
     * 根据 deviceId 和 roads 查询最新的数据
     *
     * @param deviceId 设备 ID
     * @param roads    路数
     * @return 最新的传感器数据
     */
    public SensorData queryLatestDataByDeviceIdAndRoads(String deviceId, Integer roads) {
        String fluxQuery = buildLatestSensorDataQuery(getBucketName(), deviceId, roads + "");
        QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
        List<FluxTable> tables = queryApi.query(fluxQuery);

        // 将查询结果映射为 SensorData 实体
        return tables.stream().flatMap(table -> table.getRecords().stream())  // 拉平所有记录
                .map(fluxRecord -> mapRecordToEntity(fluxRecord, getEntityClass()))  // 将每个记录映射为 SensorData 实体
                .findFirst()  // 只取第一条记录（即最新一条记录）
                .orElse(null);  // 如果没有数据，返回 null
    }

    public String buildLatestSensorDataQuery(String bucketName, String deviceId, String roads) {
        return String.format("temperature = from(bucket: \"%s\")\n" + "  |> range(start: -1h)  // Hardcoded time range for the last hour\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"temperature\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "humidity = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"humidity\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "co2 = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"co2\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "plugInStatus = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"plugInStatus\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "battery = from(bucket: \"%s\")\n" + "  |> range(start: -1h)\n" + "  |> filter(fn: (r) => r[\"_measurement\"] == \"sensor_data\" and r[\"_field\"] == \"battery\" and r[\"device_id\"] == \"%s\" and r[\"roads\"] == \"%s\")\n" + "  |> last()\n" + "union(tables: [temperature, humidity, co2, plugInStatus, battery])\n" + "  |> pivot(rowKey: [\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")\n" + "  |> yield(name: \"latest_data\")", bucketName, deviceId, roads, bucketName, deviceId, roads, bucketName, deviceId, roads, bucketName, deviceId, roads, bucketName, deviceId, roads);
    }


    /**
     * 根据 deviceId、roads 和时间范围查询数据
     *
     * @param deviceId     设备 ID
     * @param roads        路数
     * @param startTimeStr 查询开始时间
     * @param endTimeStr   查询结束时间
     * @param field        temperature/humidity/co2
     * @return 查询到的传感器数据列表
     */
    public List<SensorData> queryDataByDeviceIdAndRoads(String deviceId, Integer roads, String startTimeStr, String endTimeStr, String field, AggregationWindow aggregationWindow) {
        Assert.notNull(deviceId, "deviceId cannot be null");
        Assert.notNull(roads, "roads cannot be null");
        Assert.notNull(startTimeStr, "startTime cannot be null");
        Assert.notNull(endTimeStr, "endTime cannot be null");
        Assert.notNull(field, "field cannot be null");
        // 格式化日期并转换为ISO8601格式
        String startTimeFormatted = convertToISO8601(formatStartTime(startTimeStr));
        String endTimeFormatted = convertToISO8601(formatEndTime(endTimeStr));
        // 获取聚合窗口的代码
        String aggregationWindowCode = aggregationWindow != null ? aggregationWindow.getCode() : "default";
        // 构建查询语句
        String measurement = JfcloudColdChainConstants.INFLUXDB_DEFAULT_MEASUREMENT_NAME; // 数据表名称
        Map<String, String> filters = Map.of(
                "device_id", deviceId,
                "roads", roads.toString()
        );
        String query = FluxQueryBuilder.buildRangeQuery(getBucketName(), startTimeFormatted, endTimeFormatted, measurement, field, filters, aggregationWindowCode);
        // 执行查询并处理结果
        QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
        List<FluxTable> results = queryApi.query(query);

        return results.stream()
                .flatMap(table -> table.getRecords().stream())
                .map(fluxRecord -> mapRecordToEntity(fluxRecord, getEntityClass()))
                .collect(Collectors.toList());
    }

    /**
     * 格式化开始时间，若只有日期部分则补充时间部分为 00:00:00
     */
    private String formatStartTime(String startTimeStr) {
        if (startTimeStr == null || startTimeStr.isEmpty()) {
            throw new IllegalArgumentException("startTimeStr cannot be null or empty");
        }
        if (startTimeStr.length() == 4 && startTimeStr.matches("\\d{4}")) {
            return startTimeStr + "-01-01 00:00:00";  // Default to January 1st, 00:00:00
        }
        if (startTimeStr.length() == 7 && startTimeStr.matches("\\d{4}-\\d{2}")) {
            return startTimeStr + "-01 00:00:00";  // Default to the first day of the month, 00:00:00
        }
        if (startTimeStr.length() == 10 && startTimeStr.matches("\\d{4}-\\d{2}-\\d{2}")) {
            return startTimeStr + " 00:00:00";  // Add time if it's missing
        }
        return startTimeStr;
    }


    /**
     * 格式化结束时间，若只有日期部分则补充时间部分为 23:59:59
     */
    /**
     * 格式化结束时间，若只有日期部分则补充时间部分为 23:59:59
     */
    private String formatEndTime(String endTimeStr) {
        if (endTimeStr == null || endTimeStr.isEmpty()) {
            throw new IllegalArgumentException("endTimeStr cannot be null or empty");
        }
        if (endTimeStr.length() == 4 && endTimeStr.matches("\\d{4}")) {
            return endTimeStr + "-12-31 23:59:59";
        }
        if (endTimeStr.length() == 7 && endTimeStr.matches("\\d{4}-\\d{2}")) {
            return endTimeStr + "-31 23:59:59";
        }
        if (endTimeStr.length() == 10 && endTimeStr.matches("\\d{4}-\\d{2}-\\d{2}")) {
            return endTimeStr + " 23:59:59";
        }
        return endTimeStr;
    }



    /**
     * Converts a time string in "yyyy-MM-dd HH:mm:ss" format to ISO 8601 format.
     *
     * @param time The time string in "yyyy-MM-dd HH:mm:ss" format.
     * @return The ISO 8601 formatted time string.
     */
    public static String convertToISO8601(String time) {
        return TimeUtils.uTCPlus8ToUTC(time);
//        DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
//        DateTimeFormatter outputFormatter = DateTimeFormatter.ISO_DATE_TIME;
//        // Parse the input time string to LocalDateTime (in system default zone)
//        LocalDateTime localDateTime = LocalDateTime.parse(time, inputFormatter);
//        // Convert the LocalDateTime to Instant (UTC time zone)
//        Instant instant = localDateTime.atZone(ZoneOffset.UTC).toInstant();
//        // Format the Instant to ISO 8601
//        return outputFormatter.format(instant.atZone(ZoneOffset.UTC));
    }

    /**
     * 获取当前月的数据总数
     *
     * @param deviceId 设备ID
     * @param roads    道路字段
     * @return 当前月数据总数
     */
    public long getCurrentMonthDataCount(String deviceId, String roads) {
        // 获取当前月份的起始时间和结束时间
        String startDate = getFirstDayOfCurrentMonth();
        String endDate = getLastDayOfCurrentMonth();
        return getSensorDataCount(startDate, endDate, deviceId, roads);
    }

    /**
     * 获取上月的数据总数
     *
     * @param deviceId 设备ID
     * @param roads    道路字段
     * @return 上月数据总数
     */
    public long getLastMonthDataCount(String deviceId, String roads) {
        // 获取上个月的起始时间和结束时间
        String startDate = getFirstDayOfLastMonth();
        String endDate = getLastDayOfLastMonth();
        return getSensorDataCount(startDate, endDate, deviceId, roads);
    }

    /**
     * 获取数据总数，传入查询时间范围和参数
     *
     * @param startDate 查询开始日期
     * @param endDate   查询结束日期
     * @param deviceId  设备ID
     * @param roads     道路字段
     * @return 数据总数
     */
    private long getSensorDataCount(String startDate, String endDate, String deviceId, String roads) {
        long count = 0;
        try {
            // 构建Flux查询，简化了不必要的group操作
            String query = String.format("""
                    from(bucket: "%s")
                      |> range(start: %s, stop: %s)
                      |> filter(fn: (r) => r["_measurement"] == "%s")
                      |> filter(fn: (r) => r["device_id"] == "%s" and r["roads"] == "%s")
                      |> count()
                    """, getBucketName(), startDate, endDate, JfcloudColdChainConstants.INFLUXDB_DEFAULT_MEASUREMENT_NAME, deviceId, roads);

            // 获取QueryApi并执行查询
            QueryApi queryApi = jfcloudInfluxDBService.getInfluxDBClient().getQueryApi();
            List<FluxTable> result = queryApi.query(query);

            // 处理查询结果
            if (result != null && !result.isEmpty()) {
                FluxTable fluxTable = result.get(0);
                if (fluxTable.getRecords() != null && !fluxTable.getRecords().isEmpty()) {
                    // 获取结果值
                    Object countValue = fluxTable.getRecords().get(0).getValues().get("_value");
                    if (countValue != null) {
                        // 确保转换为正确的数字类型
                        if (countValue instanceof Number) {
                            count = ((Number) countValue).longValue();  // 类型安全转换
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace(); // 打印异常信息
        }
        return count; // 返回统计结果
    }


    /**
     * 获取当前月的起始日期
     */
    private String getFirstDayOfCurrentMonth() {
        LocalDate firstDayOfCurrentMonth = LocalDate.now().withDayOfMonth(1);
        return firstDayOfCurrentMonth.format(DateTimeFormatter.ISO_DATE) + "T00:00:00Z";
    }

    /**
     * 获取当前月的结束日期
     */
    private String getLastDayOfCurrentMonth() {
        LocalDate lastDayOfCurrentMonth = LocalDate.now().withDayOfMonth(1).plusMonths(1).minusDays(1);
        return lastDayOfCurrentMonth.format(DateTimeFormatter.ISO_DATE) + "T23:59:59Z";
    }

    /**
     * 获取上月的起始日期
     */
    private String getFirstDayOfLastMonth() {
        LocalDate firstDayOfLastMonth = LocalDate.now().minusMonths(1).withDayOfMonth(1);
        return firstDayOfLastMonth.format(DateTimeFormatter.ISO_DATE) + "T00:00:00Z";
    }

    /**
     * 获取上月的结束日期
     */
    private String getLastDayOfLastMonth() {
        LocalDate lastDayOfLastMonth = LocalDate.now().minusMonths(1).withDayOfMonth(1).plusMonths(1).minusDays(1);
        return lastDayOfLastMonth.format(DateTimeFormatter.ISO_DATE) + "T23:59:59Z";
    }
    public long getAlarmCount(String sensorCode, String sensorRoute, SensorAlarmType alarmType, boolean isLastMonth) {
        // 获取上个月或本月的起始时间和结束时间
        String startDate;
        String endDate;
        if (isLastMonth) {
            startDate = getFirstDayOfLastMonth();
            endDate = getLastDayOfLastMonth();
        } else {
            startDate = getFirstDayOfCurrentMonth();
            endDate = getLastDayOfCurrentMonth();
        }
        // 根据提供的报警类型计算报警数量
        return sensorAlarmService.countAlarms(startDate, endDate, sensorCode, sensorRoute, alarmType);
    }

    public long getLastMonthAlarmCount(String sensorCode, String sensorRoute) {
        return getAlarmCount(sensorCode, sensorRoute, SensorAlarmType.DATA_ALARM, true);
    }

    public long getCurrentMonthAlarmCount(String sensorCode, String sensorRoute) {
        return getAlarmCount(sensorCode, sensorRoute, SensorAlarmType.DATA_ALARM, false);
    }

    public long getCurrentMonthOfflineCount(String sensorCode, String sensorRoute) {
        return getAlarmCount(sensorCode, sensorRoute, SensorAlarmType.SENSOR_OFFLINE, false);
    }

    public long getLastMonthOfflineCount(String sensorCode, String sensorRoute) {
        return getAlarmCount(sensorCode, sensorRoute, SensorAlarmType.SENSOR_OFFLINE, true);
    }

}
