package com.lingniu.ingest.api.event;

import com.lingniu.ingest.api.ProtocolId;

import java.time.Instant;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
 * Normalized full-field telemetry snapshot for one parsed vehicle event.
 *
 * <p>This is the shared contract for Kafka, TDengine history, Redis hot state,
 * and statistics. It intentionally lives in {@code ingest-api} and has no
 * Spring, Kafka, Redis, or TDengine dependency.
 */
public record TelemetrySnapshot(
        String eventId,
        String vin,
        ProtocolId protocol,
        String eventType,
        Instant eventTime,
        Instant ingestTime,
        String rawArchiveUri,
        Map<String, String> metadata,
        List<TelemetryFieldValue> fields
) {

    public TelemetrySnapshot {
        if (eventId == null || eventId.isBlank()) {
            throw new IllegalArgumentException("eventId must not be blank");
        }
        if (vin == null || vin.isBlank()) {
            throw new IllegalArgumentException("vin must not be blank");
        }
        if (protocol == null) {
            throw new IllegalArgumentException("protocol must not be null");
        }
        if (eventType == null || eventType.isBlank()) {
            throw new IllegalArgumentException("eventType must not be blank");
        }
        if (eventTime == null) {
            throw new IllegalArgumentException("eventTime must not be null");
        }
        if (ingestTime == null) {
            throw new IllegalArgumentException("ingestTime must not be null");
        }
        rawArchiveUri = rawArchiveUri == null ? "" : rawArchiveUri;
        metadata = Map.copyOf(metadata == null ? Map.of() : metadata);
        fields = List.copyOf(fields == null ? List.of() : fields);
        // snapshot/fields 查询按 key 投影和导出，重复 key 会导致前端列和 CSV 表头不稳定，构造期直接拒绝。
        validateUniqueKeys(fields);
    }

    public Optional<TelemetryFieldValue> field(String key) {
        if (key == null || key.isBlank()) {
            return Optional.empty();
        }
        // fields 保持插入顺序，单字段查询走线性查找；批量查询应使用 fieldsByKey 建索引。
        for (TelemetryFieldValue field : fields) {
            if (field.key().equals(key)) {
                return Optional.of(field);
            }
        }
        return Optional.empty();
    }

    public Map<String, TelemetryFieldValue> fieldsByKey() {
        Map<String, TelemetryFieldValue> index = new LinkedHashMap<>();
        for (TelemetryFieldValue field : fields) {
            index.put(field.key(), field);
        }
        // LinkedHashMap 保留原始字段顺序，CSV 导出和 Swagger 示例能稳定复现解析器顺序。
        return Map.copyOf(index);
    }

    public static Builder builder(String eventId,
                                  String vin,
                                  ProtocolId protocol,
                                  String eventType,
                                  Instant eventTime,
                                  Instant ingestTime) {
        return new Builder(eventId, vin, protocol, eventType, eventTime, ingestTime);
    }

    private static void validateUniqueKeys(List<TelemetryFieldValue> fields) {
        Map<String, TelemetryFieldValue> seen = new LinkedHashMap<>();
        for (TelemetryFieldValue field : fields) {
            if (field == null) {
                throw new IllegalArgumentException("field must not be null");
            }
            TelemetryFieldValue previous = seen.put(field.key(), field);
            if (previous != null) {
                throw new IllegalArgumentException("duplicate telemetry field key: " + field.key());
            }
        }
    }

    public static final class Builder {
        private final String eventId;
        private final String vin;
        private final ProtocolId protocol;
        private final String eventType;
        private final Instant eventTime;
        private final Instant ingestTime;
        private String rawArchiveUri = "";
        private Map<String, String> metadata = Map.of();
        private final List<TelemetryFieldValue> fields = new ArrayList<>();

        private Builder(String eventId,
                        String vin,
                        ProtocolId protocol,
                        String eventType,
                        Instant eventTime,
                        Instant ingestTime) {
            this.eventId = eventId;
            this.vin = vin;
            this.protocol = protocol;
            this.eventType = eventType;
            this.eventTime = eventTime;
            this.ingestTime = ingestTime;
        }

        public Builder rawArchiveUri(String rawArchiveUri) {
            this.rawArchiveUri = rawArchiveUri;
            return this;
        }

        public Builder metadata(Map<String, String> metadata) {
            this.metadata = metadata == null ? Map.of() : Map.copyOf(metadata);
            return this;
        }

        public Builder addField(TelemetryFieldValue field) {
            this.fields.add(field);
            return this;
        }

        public TelemetrySnapshot build() {
            return new TelemetrySnapshot(
                    eventId, vin, protocol, eventType, eventTime, ingestTime,
                    rawArchiveUri, metadata, fields);
        }
    }
}
