package com.njcn.gather.tool.adddata.component;

import com.njcn.gather.tool.adddata.pojo.bo.AddDataInfluxFieldGroup;
import org.springframework.stereotype.Component;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.ArrayList;

/**
 * add-data 表字段到 InfluxDB 字段的映射器。
 */
@Component
public class AddDataInfluxFieldMapper {

    /** 无 value_type tag 的 measurement。 */
    private static final Set<String> NO_VALUE_TYPE_MEASUREMENTS = new LinkedHashSet<String>(
            Arrays.asList("data_flicker", "data_fluc", "data_plt"));

    /** 统计类型顺序。 */
    private static final List<String> VALUE_TYPES = Arrays.asList("AVG", "MAX", "MIN", "CP95");

    /** 派生字段后缀映射。 */
    private static final Map<String, String> STAT_SUFFIX_MAP = buildStatSuffixMap();

    /**
     * 按 InfluxDB value_type 分组映射字段。
     *
     * @param measurement measurement 名称
     * @param columns 表字段
     * @param row 行数据
     * @return 字段分组
     */
    public List<AddDataInfluxFieldGroup> mapFieldGroups(String measurement, List<String> columns, List<Object> row) {
        if (!hasValueTypeTag(measurement)) {
            return mapWithoutValueType(measurement, columns, row);
        }
        Map<String, Map<String, Object>> groupedFields = new LinkedHashMap<String, Map<String, Object>>();
        for (String valueType : VALUE_TYPES) {
            groupedFields.put(valueType, new LinkedHashMap<String, Object>());
        }
        for (int i = 0; i < columns.size(); i++) {
            String column = columns.get(i);
            if (isInfrastructureColumn(column)) {
                continue;
            }
            StatColumn statColumn = resolveStatColumn(column);
            Object value = row.get(i);
            if (value == null) {
                continue;
            }
            groupedFields.get(statColumn.valueType).put(mapFieldName(measurement, statColumn.baseColumn), value);
        }
        List<AddDataInfluxFieldGroup> result = new ArrayList<AddDataInfluxFieldGroup>();
        for (String valueType : VALUE_TYPES) {
            Map<String, Object> fields = groupedFields.get(valueType);
            if (!fields.isEmpty()) {
                result.add(new AddDataInfluxFieldGroup(valueType, fields));
            }
        }
        return result;
    }

    /**
     * 从行数据中解析 TIMEID。
     *
     * @param columns 表字段
     * @param row 行数据
     * @return 时间
     */
    public LocalDateTime resolveTimeId(List<String> columns, List<Object> row) {
        Object value = getRequiredValue(columns, row, "TIMEID");
        if (value instanceof Timestamp) {
            return ((Timestamp) value).toLocalDateTime();
        }
        if (value instanceof LocalDateTime) {
            return (LocalDateTime) value;
        }
        throw new IllegalArgumentException("TIMEID 类型不支持：" + value.getClass().getName());
    }

    /**
     * 从行数据中解析字符串字段。
     *
     * @param columns 表字段
     * @param row 行数据
     * @param columnName 字段名
     * @return 字符串值
     */
    public String resolveString(List<String> columns, List<Object> row, String columnName) {
        Object value = getRequiredValue(columns, row, columnName);
        return String.valueOf(value);
    }

    /**
     * 判断 measurement 是否带 value_type tag。
     *
     * @param measurement measurement 名称
     * @return true 表示需要写 value_type
     */
    public boolean hasValueTypeTag(String measurement) {
        return !NO_VALUE_TYPE_MEASUREMENTS.contains(measurement);
    }

    private List<AddDataInfluxFieldGroup> mapWithoutValueType(String measurement, List<String> columns, List<Object> row) {
        Map<String, Object> fields = new LinkedHashMap<String, Object>();
        for (int i = 0; i < columns.size(); i++) {
            String column = columns.get(i);
            if (isInfrastructureColumn(column) || isDerivedColumn(column)) {
                continue;
            }
            Object value = row.get(i);
            if (value != null) {
                fields.put(mapFieldName(measurement, column), value);
            }
        }
        List<AddDataInfluxFieldGroup> result = new ArrayList<AddDataInfluxFieldGroup>();
        if (!fields.isEmpty()) {
            result.add(new AddDataInfluxFieldGroup(null, fields));
        }
        return result;
    }

    private Object getRequiredValue(List<String> columns, List<Object> row, String columnName) {
        int index = columns.indexOf(columnName);
        if (index < 0 || index >= row.size() || row.get(index) == null) {
            throw new IllegalArgumentException("缺少 InfluxDB 写入字段：" + columnName);
        }
        return row.get(index);
    }

    private StatColumn resolveStatColumn(String column) {
        for (Map.Entry<String, String> entry : STAT_SUFFIX_MAP.entrySet()) {
            if (column.endsWith(entry.getKey())) {
                return new StatColumn(column.substring(0, column.length() - entry.getKey().length()), entry.getValue());
            }
        }
        return new StatColumn(column, "AVG");
    }

    private boolean isInfrastructureColumn(String column) {
        return "TIMEID".equals(column) || "LINEID".equals(column) || "PHASIC_TYPE".equals(column) || "QUALITYFLAG".equals(column);
    }

    private boolean isDerivedColumn(String column) {
        for (String suffix : STAT_SUFFIX_MAP.keySet()) {
            if (column.endsWith(suffix)) {
                return true;
            }
        }
        return false;
    }

    private String mapFieldName(String measurement, String column) {
        if ("data_v".equals(measurement) && "RMSAB".equals(column)) {
            return "rms_lvr";
        }
        return column.toLowerCase(Locale.ENGLISH);
    }

    private static Map<String, String> buildStatSuffixMap() {
        Map<String, String> result = new LinkedHashMap<String, String>();
        result.put("_MAX", "MAX");
        result.put("_MIN", "MIN");
        result.put("_CP95", "CP95");
        return result;
    }

    /**
     * 字段对应的统计类型。
     */
    private static final class StatColumn {
        /** 基础字段名。 */
        private final String baseColumn;
        /** 统计类型。 */
        private final String valueType;

        private StatColumn(String baseColumn, String valueType) {
            this.baseColumn = baseColumn;
            this.valueType = valueType;
        }
    }
}
