package com.lingniu.ingest.protocol.gb32960.model;

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

/**
 * 命令数据单元（非实时上报）。sealed 集合；每个子类型对应一条 {@link CommandType}。
 *
 * <p>实时/补发上报的数据单元不走这里，而是产出 {@link InfoBlock} 列表。
 *
 * <p>上行类型（由车载终端发起）：VehicleLogin/VehicleLogout/PlatformLogin/PlatformLogout/
 * Heartbeat/TimeCalibration/Activation/KeyExchange。
 *
 * <p>下行类型（由平台发起）：QueryParams/SetParams/TerminalControl/ActivationResponse。
 * 参见 GB/T 32960.3-2025 附录 B 表 B.5~B.13。
 */
public sealed interface CommandBody
        permits CommandBody.VehicleLogin,
                CommandBody.VehicleLogout,
                CommandBody.PlatformLogin,
                CommandBody.PlatformLogout,
                CommandBody.Heartbeat,
                CommandBody.TimeCalibration,
                CommandBody.Activation,
                CommandBody.ActivationResponse,
                CommandBody.KeyExchange,
                CommandBody.QueryParams,
                CommandBody.QueryParamsResponse,
                CommandBody.SetParams,
                CommandBody.TerminalControl,
                CommandBody.RawCommand {

    /**
     * 车辆登入 0x01（表 6）。
     *
     * @param loginTime     数据采集时间（表 5）
     * @param serialNo      登入流水号 1~65531，按天循环累加
     * @param iccid         20 字节 SIM 卡 ICCID，终端从 SIM 卡获取
     * @param batterySystemCount 电池管理系统数 n (0~20，2025 版新增)
     * @param batteryPackCounts  每个电池管理系统对应的动力蓄电池包个数
     * @param batteryCodes       动力蓄电池包编码列表（GB/T 34014，24 字节/个）
     */
    record VehicleLogin(
            Instant loginTime,
            int serialNo,
            String iccid,
            int batterySystemCount,
            List<Integer> batteryPackCounts,
            List<String> batteryCodes
    ) implements CommandBody {}

    /** 车辆登出 0x04（表 28）。 */
    record VehicleLogout(Instant logoutTime, int serialNo) implements CommandBody {}

    /** 平台登入 0x05（表 29）。 */
    record PlatformLogin(
            Instant loginTime,
            int serialNo,
            String username,
            String password,
            EncryptType encryptRule
    ) implements CommandBody {}

    /** 平台登出 0x06（表 30）。 */
    record PlatformLogout(Instant logoutTime, int serialNo) implements CommandBody {}

    /** 心跳 0x07：数据单元为空（B.3.5.10）。 */
    record Heartbeat() implements CommandBody {}

    /** 终端校时 0x08：信息标志及信息体为空（B.3.5.11）。 */
    record TimeCalibration() implements CommandBody {}

    /** 激活 0x09（表 B.3）。 */
    record Activation(
            Instant collectTime,
            String chipId,
            byte[] publicKey,
            String vin,
            byte[] signature
    ) implements CommandBody {}

    /** 激活结果应答 0x0A（表 B.4）。 */
    record ActivationResponse(int status, int info) implements CommandBody {}

    /**
     * 数据单元加密密钥交换 0x0B（表 31）。
     *
     * @param keyType    密钥类型（{@link EncryptType}）
     * @param key        密钥字节
     * @param activateAt 启用时间
     * @param expireAt   失效时间
     */
    record KeyExchange(
            EncryptType keyType,
            byte[] key,
            Instant activateAt,
            Instant expireAt
    ) implements CommandBody {}

    // ========================================================================
    // 下行命令 0x80 / 0x81 / 0x82 —— 附录 B 表 B.5 ~ B.13
    // ========================================================================

    /**
     * 查询命令 0x80（表 B.5）。平台向车载终端发送，请求查询指定参数。
     *
     * @param queryTime 参数查询时间
     * @param paramIds  待查询的参数 ID 列表（表 B.8：0x01-0x10，2025 版新增 0x0D-0x10）
     */
    record QueryParams(Instant queryTime, List<Integer> paramIds) implements CommandBody {}

    /**
     * 参数查询返回 0x80 应答（表 B.6/B.7/B.8）。车载终端响应平台。
     *
     * @param respondTime 返回查询参数时间
     * @param params      参数项键值对（key = paramId，value = bytes，需按表 B.8 解释）
     */
    record QueryParamsResponse(Instant respondTime, Map<Integer, byte[]> params) implements CommandBody {}

    /**
     * 设置命令 0x81（表 B.9）。平台向车载终端发送，修改车载终端参数。
     *
     * <p>参数 ID 范围见表 B.8，参数值定义见表 B.8 各字段说明。
     * 其中 0x07 硬件版本、0x08 固件版本为只读，不在 set 命令中出现。
     *
     * @param setTime  参数设置时间
     * @param params   参数项键值对
     */
    record SetParams(Instant setTime, Map<Integer, byte[]> params) implements CommandBody {}

    /**
     * 车载终端控制命令 0x82（表 B.10）。
     *
     * @param time      时间
     * @param commandId 控制命令（{@link ControlCommand}）
     * @param payload   根据不同控制命令的负载
     */
    record TerminalControl(
            Instant time,
            ControlCommand commandId,
            ControlPayload payload
    ) implements CommandBody {}

    /** 控制命令枚举（表 B.11）。 */
    enum ControlCommand {
        /** 0x01：远程升级，参数见表 B.12。 */
        REMOTE_UPGRADE(0x01),
        /** 0x02：车载终端关机。 */
        SHUTDOWN(0x02),
        /** 0x03：车载终端复位。 */
        RESET(0x03),
        /** 0x04：车载终端恢复出厂设置。 */
        FACTORY_RESET(0x04),
        /** 0x05：断开数据通信链路。 */
        DISCONNECT(0x05),
        /** 0x06：车载终端报警/预警，参数见表 B.13。 */
        ALARM_COMMAND(0x06),
        /** 0x07：开启抽样监测链路。 */
        SAMPLING_LINK_ON(0x07),
        /** 未知命令 / 用户自定义 0x80-0xFE。 */
        UNKNOWN(-1);

        private final int code;
        ControlCommand(int code) { this.code = code; }
        public int code() { return code; }

        public static ControlCommand of(int code) {
            for (ControlCommand c : values()) if (c.code == code) return c;
            return UNKNOWN;
        }
    }

    /** 控制命令的负载。sealed：每种控制命令对应一种 payload 类型。 */
    sealed interface ControlPayload
            permits ControlPayload.None, ControlPayload.RemoteUpgrade, ControlPayload.AlarmCommand, ControlPayload.RawPayload {

        /** 无参数（SHUTDOWN / RESET / FACTORY_RESET / DISCONNECT / SAMPLING_LINK_ON）。 */
        record None() implements ControlPayload {}

        /**
         * 远程升级参数（表 B.12）。
         *
         * @param apn              拨号点名称
         * @param dialUser         拨号用户名
         * @param dialPassword     拨号密码
         * @param serverAddress    升级服务器地址（IP 或域名，IPv4 时前 2 字节为 0）
         * @param serverPort       升级服务器端口
         * @param manufacturerCode 车载终端制造商 ID（4 字节英文大写字母或数字）
         * @param hardwareVersion  硬件版本（5 字节）
         * @param firmwareVersion  固件版本（5 字节）
         * @param upgradeUrl       升级 URL 地址（建议 FTP）
         * @param connectTimeoutMinutes 连接到升级服务器时限（0~60000 分钟）
         */
        record RemoteUpgrade(
                String apn,
                String dialUser,
                String dialPassword,
                byte[] serverAddress,
                int serverPort,
                String manufacturerCode,
                String hardwareVersion,
                String firmwareVersion,
                String upgradeUrl,
                int connectTimeoutMinutes
        ) implements ControlPayload {}

        /**
         * 报警/预警命令（表 B.13）。
         *
         * @param warningLevel 警告等级：0x00 无报警 / 0x01-0x04 1-4 级报警 / 0xFF 无效
         * @param info         预留字节，可变长
         */
        record AlarmCommand(int warningLevel, byte[] info) implements ControlPayload {}

        /** 未解析的自定义 payload（0x80-0xFE 用户自定义等）。 */
        record RawPayload(byte[] bytes) implements ControlPayload {}
    }

    /**
     * 未解析或厂商自定义的命令体（0xC0~0xFE 等）。
     * 保留原始字节以便下游冷存分析。
     */
    record RawCommand(int commandCode, byte[] bytes) implements CommandBody {}
}
