package com.njcn.gather.steady.checksquare.component;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
import com.njcn.common.pojo.exception.BusinessException;
import com.njcn.gather.steady.checksquare.pojo.bo.SteadyChecksquareValuePointBO;
import com.njcn.gather.steady.datavie.config.SteadyInfluxDbProperties;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 数据校验 InfluxDB 查询组件。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SteadyChecksquareInfluxQueryComponent {

    private static final DateTimeFormatter INFLUX_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
    private static final int QUERY_WINDOW_DAYS = 1;
    private static final ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>> REQUEST_VALUE_CACHE =
            new ThreadLocal<Map<String, List<SteadyChecksquareValuePointBO>>>();

    private final SteadyInfluxDbProperties properties;

    public void enableRequestCache() {
        REQUEST_VALUE_CACHE.set(new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>());
    }

    public void clearRequestCache() {
        REQUEST_VALUE_CACHE.remove();
    }

    public Set<LocalDateTime> queryExistingSlots(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
                                                 LocalDateTime endTime, int intervalMinutes) {
        List<SteadyChecksquareValuePointBO> points = queryValuePoints(field, startTime, endTime, intervalMinutes);
        Set<LocalDateTime> result = new HashSet<LocalDateTime>();
        for (SteadyChecksquareValuePointBO point : points) {
            if (point != null && point.getTime() != null) {
                result.add(point.getTime());
            }
        }
        return result;
    }

    public List<SteadyChecksquareValuePointBO> queryValuePoints(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
                                                                LocalDateTime endTime, int intervalMinutes) {
        validateConfig();
        String query = buildValuePointQuery(field, startTime, endTime);
        String cacheKey = buildCacheKey(query, intervalMinutes);
        Map<String, List<SteadyChecksquareValuePointBO>> cache = REQUEST_VALUE_CACHE.get();
        if (cache != null && cache.containsKey(cacheKey)) {
            return new ArrayList<SteadyChecksquareValuePointBO>(cache.get(cacheKey));
        }
        long startMillis = System.currentTimeMillis();
        log.info("数据校验指标值 InfluxDB 查询开始，measurement={}，field={}，lineId={}，phase={}，statType={}，query={}",
                field.getMeasurement(), field.getField(), field.getLineId(), field.getPhase(), field.getStatType(), query);
        try {
            List<SteadyChecksquareValuePointBO> points = queryValuePointsByWindow(field, startTime, endTime, intervalMinutes);
            if (cache != null) {
                cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
            }
            log.info("数据校验指标值 InfluxDB 查询结束，pointCount={}，costMs={}", points.size(), System.currentTimeMillis() - startMillis);
            return points;
        } catch (RuntimeException ex) {
            log.warn("数据校验指标值 InfluxDB 查询异常，costMs={}，error={}", System.currentTimeMillis() - startMillis, ex.getMessage());
            throw ex;
        }
    }

    public Map<String, List<SteadyChecksquareValuePointBO>> queryValuePointMap(List<SteadyTrendResolvedFieldBO> fields,
                                                                               LocalDateTime startTime,
                                                                               LocalDateTime endTime,
                                                                               int intervalMinutes) {
        Map<String, List<SteadyChecksquareValuePointBO>> result =
                new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
        if (fields == null || fields.isEmpty()) {
            return result;
        }
        if (fields.size() == 1) {
            SteadyTrendResolvedFieldBO field = fields.get(0);
            result.put(field.getField(), queryValuePoints(field, startTime, endTime, intervalMinutes));
            return result;
        }
        validateConfig();
        Map<String, List<SteadyChecksquareValuePointBO>> cache = REQUEST_VALUE_CACHE.get();
        List<SteadyTrendResolvedFieldBO> missingFields = new ArrayList<SteadyTrendResolvedFieldBO>();
        for (SteadyTrendResolvedFieldBO field : fields) {
            String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
            if (cache != null && cache.containsKey(cacheKey)) {
                result.put(field.getField(), new ArrayList<SteadyChecksquareValuePointBO>(cache.get(cacheKey)));
            } else {
                missingFields.add(field);
            }
        }
        if (!missingFields.isEmpty()) {
            String query = buildBatchValuePointQuery(missingFields, startTime, endTime);
            long startMillis = System.currentTimeMillis();
            SteadyTrendResolvedFieldBO first = missingFields.get(0);
            log.info("数据校验指标值 InfluxDB 批量查询开始，measurement={}，fieldCount={}，lineId={}，phase={}，statType={}，query={}",
                    first.getMeasurement(), missingFields.size(), first.getLineId(), first.getPhase(), first.getStatType(), query);
            try {
                Map<String, List<SteadyChecksquareValuePointBO>> queried =
                        queryBatchValuePointsByWindow(missingFields, startTime, endTime, intervalMinutes);
                for (SteadyTrendResolvedFieldBO field : missingFields) {
                    List<SteadyChecksquareValuePointBO> points = queried.get(field.getField());
                    if (points == null) {
                        points = new ArrayList<SteadyChecksquareValuePointBO>();
                    }
                    result.put(field.getField(), points);
                    if (cache != null) {
                        String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
                        cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
                    }
                }
                log.info("数据校验指标值 InfluxDB 批量查询结束，fieldCount={}，costMs={}",
                        missingFields.size(), System.currentTimeMillis() - startMillis);
            } catch (RuntimeException ex) {
                log.warn("数据校验指标值 InfluxDB 批量查询异常，fieldCount={}，costMs={}，error={}",
                        missingFields.size(), System.currentTimeMillis() - startMillis, ex.getMessage());
                throw ex;
            }
        }
        return result;
    }

    public Map<String, List<SteadyChecksquareValuePointBO>> queryStatValuePointMap(List<SteadyTrendResolvedFieldBO> fields,
                                                                                   LocalDateTime startTime,
                                                                                   LocalDateTime endTime,
                                                                                   int intervalMinutes) {
        Map<String, List<SteadyChecksquareValuePointBO>> result =
                new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
        if (fields == null || fields.isEmpty()) {
            return result;
        }
        if (!hasValueTypeTag(fields.get(0).getMeasurement())) {
            for (SteadyTrendResolvedFieldBO field : fields) {
                result.put(resolveValueType(field.getStatType()), queryValuePoints(field, startTime, endTime, intervalMinutes));
            }
            return result;
        }
        validateConfig();
        Map<String, List<SteadyChecksquareValuePointBO>> cache = REQUEST_VALUE_CACHE.get();
        List<SteadyTrendResolvedFieldBO> missingFields = new ArrayList<SteadyTrendResolvedFieldBO>();
        for (SteadyTrendResolvedFieldBO field : fields) {
            String statType = resolveValueType(field.getStatType());
            String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
            if (cache != null && cache.containsKey(cacheKey)) {
                result.put(statType, new ArrayList<SteadyChecksquareValuePointBO>(cache.get(cacheKey)));
            } else {
                missingFields.add(field);
            }
        }
        if (!missingFields.isEmpty()) {
            String query = buildStatValuePointQuery(missingFields, startTime, endTime);
            long startMillis = System.currentTimeMillis();
            SteadyTrendResolvedFieldBO first = missingFields.get(0);
            log.info("数据校验指标值 InfluxDB 统计类型批量查询开始，measurement={}，field={}，statCount={}，lineId={}，phase={}，query={}",
                    first.getMeasurement(), first.getField(), missingFields.size(), first.getLineId(), first.getPhase(), query);
            try {
                Map<String, List<SteadyChecksquareValuePointBO>> queried =
                        queryStatValuePointsByWindow(missingFields, startTime, endTime, intervalMinutes);
                for (SteadyTrendResolvedFieldBO field : missingFields) {
                    String statType = resolveValueType(field.getStatType());
                    List<SteadyChecksquareValuePointBO> points = queried.get(statType);
                    if (points == null) {
                        points = new ArrayList<SteadyChecksquareValuePointBO>();
                    }
                    result.put(statType, points);
                    if (cache != null) {
                        String cacheKey = buildCacheKey(buildValuePointQuery(field, startTime, endTime), intervalMinutes);
                        cache.put(cacheKey, new ArrayList<SteadyChecksquareValuePointBO>(points));
                    }
                }
                log.info("数据校验指标值 InfluxDB 统计类型批量查询结束，statCount={}，costMs={}",
                        missingFields.size(), System.currentTimeMillis() - startMillis);
            } catch (RuntimeException ex) {
                log.warn("数据校验指标值 InfluxDB 统计类型批量查询异常，statCount={}，costMs={}，error={}",
                        missingFields.size(), System.currentTimeMillis() - startMillis, ex.getMessage());
                throw ex;
            }
        }
        return result;
    }

    public String buildChecksquareQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
        return buildValuePointQuery(field, startTime, endTime);
    }

    private String buildCacheKey(String query, int intervalMinutes) {
        return query + "|intervalMinutes=" + intervalMinutes;
    }

    private List<SteadyChecksquareValuePointBO> queryValuePointsByWindow(SteadyTrendResolvedFieldBO field,
                                                                          LocalDateTime startTime,
                                                                          LocalDateTime endTime,
                                                                          int intervalMinutes) {
        List<SteadyChecksquareValuePointBO> result = new ArrayList<SteadyChecksquareValuePointBO>();
        LocalDateTime windowStart = startTime;
        while (!windowStart.isAfter(endTime)) {
            LocalDateTime windowEnd = min(windowStart.plusDays(QUERY_WINDOW_DAYS).minusNanos(1), endTime);
            result.addAll(parseValuePoints(executeQuery(buildValuePointQuery(field, windowStart, windowEnd)), intervalMinutes));
            windowStart = windowEnd.plusNanos(1);
        }
        return result;
    }

    private Map<String, List<SteadyChecksquareValuePointBO>> queryBatchValuePointsByWindow(List<SteadyTrendResolvedFieldBO> fields,
                                                                                           LocalDateTime startTime,
                                                                                           LocalDateTime endTime,
                                                                                           int intervalMinutes) {
        Map<String, List<SteadyChecksquareValuePointBO>> result =
                new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
        for (SteadyTrendResolvedFieldBO field : fields) {
            result.put(field.getField(), new ArrayList<SteadyChecksquareValuePointBO>());
        }
        LocalDateTime windowStart = startTime;
        while (!windowStart.isAfter(endTime)) {
            LocalDateTime windowEnd = min(windowStart.plusDays(QUERY_WINDOW_DAYS).minusNanos(1), endTime);
            Map<String, List<SteadyChecksquareValuePointBO>> windowResult =
                    parseBatchValuePoints(executeQuery(buildBatchValuePointQuery(fields, windowStart, windowEnd)), intervalMinutes);
            for (Map.Entry<String, List<SteadyChecksquareValuePointBO>> entry : windowResult.entrySet()) {
                List<SteadyChecksquareValuePointBO> points = result.get(entry.getKey());
                if (points == null) {
                    points = new ArrayList<SteadyChecksquareValuePointBO>();
                    result.put(entry.getKey(), points);
                }
                points.addAll(entry.getValue());
            }
            windowStart = windowEnd.plusNanos(1);
        }
        return result;
    }

    private Map<String, List<SteadyChecksquareValuePointBO>> queryStatValuePointsByWindow(List<SteadyTrendResolvedFieldBO> fields,
                                                                                          LocalDateTime startTime,
                                                                                          LocalDateTime endTime,
                                                                                          int intervalMinutes) {
        Map<String, List<SteadyChecksquareValuePointBO>> result =
                new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
        for (SteadyTrendResolvedFieldBO field : fields) {
            result.put(resolveValueType(field.getStatType()), new ArrayList<SteadyChecksquareValuePointBO>());
        }
        LocalDateTime windowStart = startTime;
        while (!windowStart.isAfter(endTime)) {
            LocalDateTime windowEnd = min(windowStart.plusDays(QUERY_WINDOW_DAYS).minusNanos(1), endTime);
            Map<String, List<SteadyChecksquareValuePointBO>> windowResult =
                    parseStatValuePoints(executeQuery(buildStatValuePointQuery(fields, windowStart, windowEnd)), intervalMinutes);
            for (Map.Entry<String, List<SteadyChecksquareValuePointBO>> entry : windowResult.entrySet()) {
                List<SteadyChecksquareValuePointBO> points = result.get(entry.getKey());
                if (points == null) {
                    points = new ArrayList<SteadyChecksquareValuePointBO>();
                    result.put(entry.getKey(), points);
                }
                points.addAll(entry.getValue());
            }
            windowStart = windowEnd.plusNanos(1);
        }
        return result;
    }

    private LocalDateTime min(LocalDateTime first, LocalDateTime second) {
        return first.isAfter(second) ? second : first;
    }

    public String buildValuePointQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime) {
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT \"").append(field.getField()).append("\" AS \"value\"");
        sql.append(" FROM \"").append(field.getMeasurement()).append("\"");
        sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'");
        sql.append(" AND time <= '").append(INFLUX_TIME_FORMATTER.format(endTime)).append("'");
        sql.append(" AND \"line_id\" = '").append(escapeTagValue(field.getLineId())).append("'");
        sql.append(" AND \"phasic_type\" = '").append(escapeTagValue(field.getPhase())).append("'");
        if (hasValueTypeTag(field.getMeasurement())) {
            sql.append(" AND \"value_type\" = '").append(resolveValueType(field.getStatType())).append("'");
        }
        sql.append(" ORDER BY time ASC");
        return sql.toString();
    }

    public String buildBatchValuePointQuery(List<SteadyTrendResolvedFieldBO> fields, LocalDateTime startTime, LocalDateTime endTime) {
        SteadyTrendResolvedFieldBO first = fields.get(0);
        StringBuilder sql = new StringBuilder("SELECT ");
        for (int i = 0; i < fields.size(); i++) {
            SteadyTrendResolvedFieldBO field = fields.get(i);
            if (i > 0) {
                sql.append(", ");
            }
            sql.append("\"").append(field.getField()).append("\" AS \"").append(field.getField()).append("\"");
        }
        sql.append(" FROM \"").append(first.getMeasurement()).append("\"");
        sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'");
        sql.append(" AND time <= '").append(INFLUX_TIME_FORMATTER.format(endTime)).append("'");
        sql.append(" AND \"line_id\" = '").append(escapeTagValue(first.getLineId())).append("'");
        sql.append(" AND \"phasic_type\" = '").append(escapeTagValue(first.getPhase())).append("'");
        if (hasValueTypeTag(first.getMeasurement())) {
            sql.append(" AND \"value_type\" = '").append(resolveValueType(first.getStatType())).append("'");
        }
        sql.append(" ORDER BY time ASC");
        return sql.toString();
    }

    public String buildStatValuePointQuery(List<SteadyTrendResolvedFieldBO> fields, LocalDateTime startTime, LocalDateTime endTime) {
        SteadyTrendResolvedFieldBO first = fields.get(0);
        StringBuilder sql = new StringBuilder();
        sql.append("SELECT \"").append(first.getField()).append("\" AS \"value\"");
        sql.append(" FROM \"").append(first.getMeasurement()).append("\"");
        sql.append(" WHERE time >= '").append(INFLUX_TIME_FORMATTER.format(startTime)).append("'");
        sql.append(" AND time <= '").append(INFLUX_TIME_FORMATTER.format(endTime)).append("'");
        sql.append(" AND \"line_id\" = '").append(escapeTagValue(first.getLineId())).append("'");
        sql.append(" AND \"phasic_type\" = '").append(escapeTagValue(first.getPhase())).append("'");
        sql.append(" AND \"value_type\" =~ /^(");
        for (int i = 0; i < fields.size(); i++) {
            if (i > 0) {
                sql.append("|");
            }
            sql.append(escapeRegexValue(resolveValueType(fields.get(i).getStatType())));
        }
        sql.append(")$/");
        sql.append(" GROUP BY \"value_type\"");
        sql.append(" ORDER BY time ASC");
        return sql.toString();
    }

    private List<SteadyChecksquareValuePointBO> parseValuePoints(String body, int intervalMinutes) {
        try {
            JsonNode root = OBJECT_MAPPER.readTree(body);
            JsonNode values = root.path("results").path(0).path("series").path(0).path("values");
            List<SteadyChecksquareValuePointBO> result = new ArrayList<SteadyChecksquareValuePointBO>();
            if (!values.isArray()) {
                return result;
            }
            for (JsonNode value : values) {
                if (value.size() < 2 || value.get(1).isNull()) {
                    continue;
                }
                LocalDateTime time = parseInfluxTime(value.get(0).asText());
                if (time == null) {
                    continue;
                }
                SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
                point.setTime(alignToPreviousSlot(time, intervalMinutes));
                point.setValue(new BigDecimal(value.get(1).asText()));
                result.add(point);
            }
            return result;
        } catch (IOException ex) {
            throw fail("InfluxDB 返回结果解析失败：" + ex.getMessage());
        } catch (NumberFormatException ex) {
            throw fail("InfluxDB 返回指标值格式不正确：" + ex.getMessage());
        }
    }

    private Map<String, List<SteadyChecksquareValuePointBO>> parseBatchValuePoints(String body, int intervalMinutes) {
        try {
            JsonNode root = OBJECT_MAPPER.readTree(body);
            JsonNode series = root.path("results").path(0).path("series").path(0);
            JsonNode columns = series.path("columns");
            JsonNode values = series.path("values");
            Map<Integer, String> columnMap = new LinkedHashMap<Integer, String>();
            Map<String, List<SteadyChecksquareValuePointBO>> result =
                    new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
            if (!columns.isArray() || !values.isArray()) {
                return result;
            }
            for (int i = 1; i < columns.size(); i++) {
                String fieldName = columns.get(i).asText();
                columnMap.put(i, fieldName);
                result.put(fieldName, new ArrayList<SteadyChecksquareValuePointBO>());
            }
            for (JsonNode row : values) {
                if (row.size() < 2) {
                    continue;
                }
                LocalDateTime time = parseInfluxTime(row.get(0).asText());
                if (time == null) {
                    continue;
                }
                LocalDateTime slot = alignToPreviousSlot(time, intervalMinutes);
                for (Map.Entry<Integer, String> entry : columnMap.entrySet()) {
                    JsonNode value = row.get(entry.getKey());
                    if (value == null || value.isNull()) {
                        continue;
                    }
                    SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
                    point.setTime(slot);
                    point.setValue(new BigDecimal(value.asText()));
                    result.get(entry.getValue()).add(point);
                }
            }
            return result;
        } catch (IOException ex) {
            throw fail("InfluxDB 返回结果解析失败：" + ex.getMessage());
        } catch (NumberFormatException ex) {
            throw fail("InfluxDB 返回指标值格式不正确：" + ex.getMessage());
        }
    }

    private Map<String, List<SteadyChecksquareValuePointBO>> parseStatValuePoints(String body, int intervalMinutes) {
        try {
            JsonNode root = OBJECT_MAPPER.readTree(body);
            JsonNode seriesArray = root.path("results").path(0).path("series");
            Map<String, List<SteadyChecksquareValuePointBO>> result =
                    new LinkedHashMap<String, List<SteadyChecksquareValuePointBO>>();
            if (!seriesArray.isArray()) {
                return result;
            }
            for (JsonNode series : seriesArray) {
                String statType = series.path("tags").path("value_type").asText(null);
                if (statType == null || statType.trim().isEmpty()) {
                    continue;
                }
                statType = resolveValueType(statType);
                List<SteadyChecksquareValuePointBO> points = result.get(statType);
                if (points == null) {
                    points = new ArrayList<SteadyChecksquareValuePointBO>();
                    result.put(statType, points);
                }
                JsonNode values = series.path("values");
                if (!values.isArray()) {
                    continue;
                }
                for (JsonNode value : values) {
                    if (value.size() < 2 || value.get(1).isNull()) {
                        continue;
                    }
                    LocalDateTime time = parseInfluxTime(value.get(0).asText());
                    if (time == null) {
                        continue;
                    }
                    SteadyChecksquareValuePointBO point = new SteadyChecksquareValuePointBO();
                    point.setTime(alignToPreviousSlot(time, intervalMinutes));
                    point.setValue(new BigDecimal(value.get(1).asText()));
                    points.add(point);
                }
            }
            return result;
        } catch (IOException ex) {
            throw fail("InfluxDB 返回结果解析失败：" + ex.getMessage());
        } catch (NumberFormatException ex) {
            throw fail("InfluxDB 返回指标值格式不正确：" + ex.getMessage());
        }
    }

    private LocalDateTime alignToPreviousSlot(LocalDateTime time, int intervalMinutes) {
        LocalDateTime minuteFloor = time.withSecond(0).withNano(0);
        int minuteOfDay = minuteFloor.getHour() * 60 + minuteFloor.getMinute();
        int remainder = minuteOfDay % intervalMinutes;
        return minuteFloor.minusMinutes(remainder);
    }

    private LocalDateTime parseInfluxTime(String value) {
        try {
            return OffsetDateTime.parse(value).withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime();
        } catch (RuntimeException ex) {
            return null;
        }
    }

    private String executeQuery(String query) {
        HttpURLConnection connection = null;
        try {
            URL url = new URL(buildQueryUrl(query));
            connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("GET");
            connection.setConnectTimeout(properties.getConnectTimeoutMs());
            connection.setReadTimeout(properties.getReadTimeoutMs());
            int status = connection.getResponseCode();
            InputStream stream = status >= 200 && status < 300 ? connection.getInputStream() : connection.getErrorStream();
            String body = readBody(stream);
            if (status < 200 || status >= 300) {
                throw fail("InfluxDB 查询失败：" + body);
            }
            return body;
        } catch (IOException ex) {
            throw fail("InfluxDB 查询异常：" + ex.getMessage());
        } finally {
            if (connection != null) {
                connection.disconnect();
            }
        }
    }

    private String buildQueryUrl(String query) throws IOException {
        StringBuilder url = new StringBuilder(trimRightSlash(properties.getUrl())).append("/query?");
        url.append("db=").append(encode(properties.getDatabase()));
        if (properties.getUsername() != null && !properties.getUsername().trim().isEmpty()) {
            url.append("&u=").append(encode(properties.getUsername().trim()));
        }
        if (properties.getPassword() != null && !properties.getPassword().trim().isEmpty()) {
            url.append("&p=").append(encode(properties.getPassword()));
        }
        url.append("&q=").append(encode(query));
        return url.toString();
    }

    private void validateConfig() {
        if (properties.getUrl() == null || properties.getUrl().trim().isEmpty()) {
            throw fail("InfluxDB 地址未配置");
        }
        if (properties.getDatabase() == null || properties.getDatabase().trim().isEmpty()) {
            throw fail("InfluxDB database 未配置");
        }
    }

    private String readBody(InputStream stream) throws IOException {
        if (stream == null) {
            return "";
        }
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8));
        StringBuilder body = new StringBuilder();
        String line;
        while ((line = reader.readLine()) != null) {
            body.append(line);
        }
        return body.toString();
    }

    private String escapeTagValue(String value) {
        return value == null ? "" : value.replace("\\", "\\\\").replace("'", "\\'");
    }

    private String escapeRegexValue(String value) {
        return value == null ? "" : value.replace("\\", "\\\\")
                .replace("|", "\\|")
                .replace("(", "\\(")
                .replace(")", "\\)")
                .replace("^", "\\^")
                .replace("$", "\\$")
                .replace(".", "\\.");
    }

    private String resolveValueType(String statType) {
        if (statType == null || statType.trim().isEmpty()) {
            return "AVG";
        }
        return statType.trim().toUpperCase();
    }

    private boolean hasValueTypeTag(String measurement) {
        return !"data_flicker".equals(measurement) && !"data_fluc".equals(measurement) && !"data_plt".equals(measurement);
    }

    private String trimRightSlash(String value) {
        String text = value.trim();
        while (text.endsWith("/")) {
            text = text.substring(0, text.length() - 1);
        }
        return text;
    }

    private String encode(String value) throws IOException {
        return URLEncoder.encode(value, StandardCharsets.UTF_8.name());
    }

    private BusinessException fail(String message) {
        return new BusinessException(CommonResponseEnum.FAIL, message);
    }
}
