package com.lingniu.ingest.api.event;

import com.lingniu.ingest.api.ProtocolId;

import java.time.Instant;
import java.util.Map;

/**
 * 领域事件根类型。所有协议归一到这里。
 *
 * <p>使用 sealed + record 表达有界闭合的事件集合，下游消费方可用 {@code switch} 模式匹配穷尽处理。
 *
 * <p>字段约定：
 * <ul>
 *   <li>{@link #vin} 作为 Kafka 分区 key，保证单车顺序
 *   <li>{@link #eventTime} 设备上报时间（而非服务器接收时间）
 *   <li>{@link #ingestTime} 服务器接收时间，用于延迟监控
 *   <li>{@link #traceId} W3C traceparent 上下文
 * </ul>
 */
public sealed interface VehicleEvent
        permits VehicleEvent.Realtime,
                VehicleEvent.Location,
                VehicleEvent.Alarm,
                VehicleEvent.Login,
                VehicleEvent.Logout,
                VehicleEvent.Heartbeat,
                VehicleEvent.MediaMeta,
                VehicleEvent.Passthrough,
                VehicleEvent.RawArchive {

    String eventId();
    String vin();
    ProtocolId source();
    Instant eventTime();
    Instant ingestTime();
    String traceId();

    /** 通用元数据：协议版本、终端手机号、网关 IP 等。 */
    Map<String, String> metadata();

    // ===== 具体事件类型 =====

    /** 整车实时数据（32960 实时上报 / MQTT 宇通 / JT808 位置扩展等都归一到这里）。 */
    record Realtime(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata,
            RealtimePayload payload
    ) implements VehicleEvent {}

    /** 纯位置事件（808 的 0x0200 / Xinda 0200 映射到此）。 */
    record Location(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata,
            LocationPayload payload
    ) implements VehicleEvent {}

    /** 报警事件。 */
    record Alarm(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata,
            AlarmPayload payload
    ) implements VehicleEvent {}

    /** 设备登入 / 车辆登入。 */
    record Login(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata,
            String iccid,
            String protocolVersion
    ) implements VehicleEvent {}

    /** 设备登出。 */
    record Logout(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata
    ) implements VehicleEvent {}

    /** 心跳 / 链路保活。 */
    record Heartbeat(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata
    ) implements VehicleEvent {}

    /** 多媒体元数据（大文件本体走对象存储，通过 archiveRef 引用）。 */
    record MediaMeta(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata,
            String mediaId,
            String mediaType,
            long sizeBytes,
            String archiveRef
    ) implements VehicleEvent {}

    /** 透传 / 自定义扩展消息。 */
    record Passthrough(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata,
            int passthroughType,
            byte[] data
    ) implements VehicleEvent {}

    /**
     * 原始报文归档事件：每条成功解码的入站帧由 Dispatcher 产出一条，携带原始字节和归档索引信息。
     * 32960 历史全字段查询依赖它对应的 {@code archive://...} 文件能够被回读。
     *
     * <p>当前 {@code KafkaEventSink} 会发送本类型的索引信息，RAW bytes 由 archive 落盘保存；
     * 因此启用 raw 落盘/转发实现时，必须同时保证 {@link RawArchiveKeys} 的 key 规则一致。
     *
     * @param command  协议主命令码（如 32960 0x02/0x03 等），冗余在事件里便于按命令分片归档
     * @param infoType 协议子类型（可为 0），同上
     * @param rawBytes 原始入站字节，不可为 null
     * @param parsedJson 已解析结构化 JSON。只用于 RAW 明细查询，不应复制到位置/字段等派生表。
     */
    record RawArchive(
            String eventId,
            String vin,
            ProtocolId source,
            Instant eventTime,
            Instant ingestTime,
            String traceId,
            Map<String, String> metadata,
            int command,
            int infoType,
            byte[] rawBytes,
            String parsedJson
    ) implements VehicleEvent {
        public RawArchive(String eventId,
                          String vin,
                          ProtocolId source,
                          Instant eventTime,
                          Instant ingestTime,
                          String traceId,
                          Map<String, String> metadata,
                          int command,
                          int infoType,
                          byte[] rawBytes) {
            this(eventId, vin, source, eventTime, ingestTime, traceId, metadata,
                    command, infoType, rawBytes, "");
        }

        public RawArchive {
            parsedJson = parsedJson == null ? "" : parsedJson;
        }
    }
}
