package com.lingniu.ingest.sink.kafka;

import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

@ConfigurationProperties(prefix = "lingniu.ingest.sink.kafka")
public class KafkaSinkProperties {

    /**
     * Kafka Sink 总开关。默认 {@code true}。
     * 设为 {@code false} 时 {@link KafkaSinkAutoConfiguration} 不装配 Kafka Producer、
     * EnvelopeMapper、TopicRouter、KafkaEventSink 等生产端组件。生产接入应用保持默认开启，
     * 只有专门的查询/测试运行时才应关闭 Kafka sink。
     */
    private boolean enabled = true;

    /** Kafka bootstrap servers；生产环境应通过环境变量覆盖，不建议使用默认开发地址。 */
    private String bootstrapServers = "114.55.58.251:9092";
    private String compressionType = "zstd";
    private int lingerMs = 20;
    private int batchSize = 65536;
    private String acks = "all";
    private boolean enableIdempotence = true;
    /** 写入 envelope 的节点标识，进入 Protobuf 字段 ingest_node_id，便于追踪多实例来源。 */
    private String nodeId = "ingest-local";
    private Topics topics = new Topics();
    private Consumer consumer = new Consumer();

    public boolean isEnabled() { return enabled; }
    public void setEnabled(boolean enabled) { this.enabled = enabled; }
    public String getBootstrapServers() { return bootstrapServers; }
    public void setBootstrapServers(String bootstrapServers) { this.bootstrapServers = bootstrapServers; }
    public String getCompressionType() { return compressionType; }
    public void setCompressionType(String compressionType) { this.compressionType = compressionType; }
    public int getLingerMs() { return lingerMs; }
    public void setLingerMs(int lingerMs) { this.lingerMs = lingerMs; }
    public int getBatchSize() { return batchSize; }
    public void setBatchSize(int batchSize) { this.batchSize = batchSize; }
    public String getAcks() { return acks; }
    public void setAcks(String acks) { this.acks = acks; }
    public boolean isEnableIdempotence() { return enableIdempotence; }
    public void setEnableIdempotence(boolean enableIdempotence) { this.enableIdempotence = enableIdempotence; }
    public String getNodeId() { return nodeId; }
    public void setNodeId(String nodeId) { this.nodeId = nodeId; }
    public Topics getTopics() { return topics; }
    public void setTopics(Topics topics) { this.topics = topics; }
    public Consumer getConsumer() { return consumer; }
    public void setConsumer(Consumer consumer) { this.consumer = consumer; }

    public static class Topics {
        /** 实时遥测事件 topic；GB32960 RAW-only 架构下可逐步弱化该 topic。 */
        private String realtime = "vehicle.event.gb32960.v1";
        /** 位置事件 topic；通常可由 realtime/RAW 派生。 */
        private String location = "vehicle.event.gb32960.v1";
        private String alarm = "vehicle.event.gb32960.v1";
        private String session = "vehicle.event.gb32960.v1";
        private String mediaMeta = "vehicle.media.meta.v1";
        /** RAW 归档引用 topic，payload 应携带 archive URI/size，不建议携带完整 raw bytes。 */
        private String rawArchive = "vehicle.raw.gb32960.v1";
        /** producer 熔断或 consumer 处理失败时的死信 topic。 */
        private String dlq = "vehicle.dlq.gb32960.v1";

        public String getRealtime() { return realtime; }
        public void setRealtime(String realtime) { this.realtime = realtime; }
        public String getLocation() { return location; }
        public void setLocation(String location) { this.location = location; }
        public String getAlarm() { return alarm; }
        public void setAlarm(String alarm) { this.alarm = alarm; }
        public String getSession() { return session; }
        public void setSession(String session) { this.session = session; }
        public String getMediaMeta() { return mediaMeta; }
        public void setMediaMeta(String mediaMeta) { this.mediaMeta = mediaMeta; }
        public String getRawArchive() { return rawArchive; }
        public void setRawArchive(String rawArchive) { this.rawArchive = rawArchive; }
        public String getDlq() { return dlq; }
        public void setDlq(String dlq) { this.dlq = dlq; }
    }

    public static class Consumer {
        /** Kafka 消费总开关。与 producer 总开关分离，默认 false，避免单体服务意外自消费。 */
        private boolean enabled = false;
        private boolean autoStartup = true;
        private String clientIdPrefix = "lingniu-envelope-consumer";
        private int pollTimeoutMillis = 1000;
        private int loopBackoffMillis = 1000;
        private String autoOffsetReset = "earliest";
        private int maxPollRecords = 500;
        private int maxPollIntervalMillis = 1800000;
        private int concurrency = 1;
        /**
         * Processor bean name -> Kafka binding。
         *
         * <p>示例 key：{@code eventHistoryEnvelopeConsumerProcessor}、
         * {@code vehicleStateEnvelopeConsumerProcessor}、{@code vehicleStatEnvelopeConsumerProcessor}。
         */
        private Map<String, Binding> bindings = new LinkedHashMap<>();

        public boolean isEnabled() { return enabled; }
        public void setEnabled(boolean enabled) { this.enabled = enabled; }
        public boolean isAutoStartup() { return autoStartup; }
        public void setAutoStartup(boolean autoStartup) { this.autoStartup = autoStartup; }
        public String getClientIdPrefix() { return clientIdPrefix; }
        public void setClientIdPrefix(String clientIdPrefix) { this.clientIdPrefix = clientIdPrefix; }
        public int getPollTimeoutMillis() { return pollTimeoutMillis; }
        public void setPollTimeoutMillis(int pollTimeoutMillis) { this.pollTimeoutMillis = pollTimeoutMillis; }
        public int getLoopBackoffMillis() { return loopBackoffMillis; }
        public void setLoopBackoffMillis(int loopBackoffMillis) { this.loopBackoffMillis = loopBackoffMillis; }
        public String getAutoOffsetReset() { return autoOffsetReset; }
        public void setAutoOffsetReset(String autoOffsetReset) { this.autoOffsetReset = autoOffsetReset; }
        public int getMaxPollRecords() { return maxPollRecords; }
        public void setMaxPollRecords(int maxPollRecords) { this.maxPollRecords = maxPollRecords; }
        public int getMaxPollIntervalMillis() { return maxPollIntervalMillis; }
        public void setMaxPollIntervalMillis(int maxPollIntervalMillis) { this.maxPollIntervalMillis = maxPollIntervalMillis; }
        public int getConcurrency() { return concurrency; }
        public void setConcurrency(int concurrency) { this.concurrency = concurrency; }
        public Map<String, Binding> getBindings() { return bindings; }
        public void setBindings(Map<String, Binding> bindings) { this.bindings = bindings; }
    }

    public static class Binding {
        /** 单个 processor binding 开关，用于临时停某个下游消费者而不关整个 consumer runner。 */
        private boolean enabled = true;
        /** Kafka consumer group id。不同服务要独立消费同一 topic 时必须使用不同 group。 */
        private String groupId;
        /** 该 processor 订阅的 topic 列表。 */
        private List<String> topics = new ArrayList<>();

        public boolean isEnabled() { return enabled; }
        public void setEnabled(boolean enabled) { this.enabled = enabled; }
        public String getGroupId() { return groupId; }
        public void setGroupId(String groupId) { this.groupId = groupId; }
        public List<String> getTopics() { return topics; }
        public void setTopics(List<String> topics) { this.topics = topics; }
    }
}
