package com.njcn.gather.steady.datavie.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.datavie.config.SteadyInfluxDbProperties;
import com.njcn.gather.steady.datavie.pojo.bo.SteadyTrendResolvedFieldBO;
import com.njcn.gather.steady.datavie.pojo.vo.SteadyTrendPointVO;
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.List;

/**
 * 稳态趋势 InfluxDB 查询组件。
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class SteadyInfluxQueryComponent {

    private static final DateTimeFormatter INFLUX_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss'Z'");
    private static final DateTimeFormatter OUTPUT_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

    private final SteadyInfluxDbProperties properties;

    public List<SteadyTrendPointVO> queryTrendPoints(SteadyTrendResolvedFieldBO field, LocalDateTime startTime,
                                                     LocalDateTime endTime, Integer qualityFlag) {
        validateConfig();
        String query = buildTrendQuery(field, startTime, endTime, qualityFlag);
        String diagnostic = buildTrendQueryDiagnostic(field, startTime, endTime, qualityFlag);
        long startMillis = System.currentTimeMillis();
        log.info("稳态趋势 InfluxDB 查询开始，{}，query={}", diagnostic, query);
        try {
            String body = executeQuery(query);
            List<SteadyTrendPointVO> points = parseTrendPoints(body);
            log.info("稳态趋势 InfluxDB 查询结束，{}，pointCount={}，costMs={}", diagnostic, points.size(), System.currentTimeMillis() - startMillis);
            return points;
        } catch (RuntimeException ex) {
            log.warn("稳态趋势 InfluxDB 查询异常，{}，costMs={}，error={}", diagnostic, System.currentTimeMillis() - startMillis, ex.getMessage());
            throw ex;
        }
    }

    String buildTrendQueryDiagnostic(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime,
                                     Integer qualityFlag) {
        StringBuilder diagnostic = new StringBuilder();
        diagnostic.append("measurement=").append(field.getMeasurement());
        diagnostic.append(", field=").append(field.getField());
        diagnostic.append(", lineId=").append(field.getLineId());
        diagnostic.append(", phase=").append(field.getPhase());
        diagnostic.append(", statType=").append(resolveValueType(field.getStatType()));
        diagnostic.append(", qualityFlag=").append(qualityFlag);
        diagnostic.append(", timeStart=").append(startTime);
        diagnostic.append(", timeEnd=").append(endTime);
        return diagnostic.toString();
    }

    public String buildTrendQuery(SteadyTrendResolvedFieldBO field, LocalDateTime startTime, LocalDateTime endTime,
                                  Integer qualityFlag) {
        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("'");
        }
        if (qualityFlag != null) {
            sql.append(" AND \"quality_flag\" = '").append(qualityFlag).append("'");
        }
        sql.append(" ORDER BY time ASC");
        return sql.toString();
    }

    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 List<SteadyTrendPointVO> parseTrendPoints(String body) {
        try {
            JsonNode root = OBJECT_MAPPER.readTree(body);
            JsonNode series = root.path("results").path(0).path("series").path(0);
            JsonNode values = series.path("values");
            if (!values.isArray()) {
                return new ArrayList<SteadyTrendPointVO>();
            }
            List<SteadyTrendPointVO> result = new ArrayList<SteadyTrendPointVO>();
            for (JsonNode value : values) {
                if (value.size() < 2 || value.get(1).isNull()) {
                    continue;
                }
                String time = formatInfluxTime(value.get(0).asText());
                BigDecimal pointValue = value.get(1).decimalValue();
                result.add(new SteadyTrendPointVO(time, pointValue));
            }
            return result;
        } catch (IOException ex) {
            throw fail("InfluxDB 返回结果解析失败：" + ex.getMessage());
        }
    }

    private String formatInfluxTime(String value) {
        try {
            return OUTPUT_TIME_FORMATTER.format(OffsetDateTime.parse(value).withOffsetSameInstant(ZoneOffset.UTC).toLocalDateTime());
        } catch (RuntimeException ex) {
            return value;
        }
    }

    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 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);
    }
}
