package com.lingniu.ingest.eventhistory;

import com.google.protobuf.InvalidProtocolBufferException;
import com.lingniu.ingest.api.consumer.EnvelopeBatchIngestor;
import com.lingniu.ingest.api.consumer.EnvelopeIngestResult;
import com.lingniu.ingest.sink.kafka.proto.VehicleEnvelope;
import com.lingniu.ingest.tdenginehistory.TdengineEnvelopeRows;
import com.lingniu.ingest.tdenginehistory.TdengineHistoryWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Consumer-side entry point that stores Kafka envelope values into TDengine history tables.
 *
 * <p>当前 32960 本机运行配置没有启用 Kafka consumer；这个类保留给“其他服务把 envelope
 * 写回历史库”的解耦部署方式。失败时返回结构化结果，由 Kafka worker 决定是否进 DLQ。
 */
public final class EventHistoryEnvelopeIngestor implements EnvelopeBatchIngestor {

    private static final Logger log = LoggerFactory.getLogger(EventHistoryEnvelopeIngestor.class);

    private final TdengineHistoryWriter tdengineWriter;

    public EventHistoryEnvelopeIngestor(TdengineHistoryWriter tdengineWriter) {
        if (tdengineWriter == null) {
            throw new IllegalArgumentException("tdengineWriter must not be null");
        }
        this.tdengineWriter = tdengineWriter;
    }

    public void ingest(byte[] kafkaValue) throws IOException {
        VehicleEnvelope envelope = parse(kafkaValue);
        writeTdengineFacts(List.of(envelope));
    }

    @Override
    public EnvelopeIngestResult tryIngest(byte[] kafkaValue) {
        return tryIngestAll(List.of(kafkaValue)).getFirst();
    }

    @Override
    public List<EnvelopeIngestResult> tryIngestAll(List<byte[]> kafkaValues) {
        if (kafkaValues == null || kafkaValues.isEmpty()) {
            return List.of();
        }
        List<EnvelopeIngestResult> results = new ArrayList<>(kafkaValues.size());
        List<BatchEntry> valid = new ArrayList<>(kafkaValues.size());
        for (byte[] kafkaValue : kafkaValues) {
            VehicleEnvelope envelope = null;
            try {
                envelope = parse(kafkaValue);
                results.add(null);
                valid.add(new BatchEntry(results.size() - 1, envelope));
            } catch (IllegalArgumentException ex) {
                results.add(envelope == null
                        ? EnvelopeIngestResult.invalid(ex.getMessage())
                        : EnvelopeIngestResult.skipped(envelope.getEventId(), envelope.getVin(), ex.getMessage()));
            }
        }
        if (valid.isEmpty()) {
            return List.copyOf(results);
        }
        try {
            writeTdengineFacts(valid.stream().map(BatchEntry::envelope).toList());
            for (BatchEntry entry : valid) {
                results.set(entry.index(), EnvelopeIngestResult.stored(
                        entry.eventId(),
                        entry.vin()));
            }
        } catch (IOException ex) {
            log.error("event history TDengine ingest failed batchSize={} validSize={} firstEventId={} firstVin={}",
                    kafkaValues.size(),
                    valid.size(),
                    valid.getFirst().envelope().getEventId(),
                    valid.getFirst().envelope().getVin(),
                    ex);
            for (BatchEntry entry : valid) {
                results.set(entry.index(), EnvelopeIngestResult.failed(
                        entry.envelope().getEventId(),
                        entry.envelope().getVin(),
                        ex.getMessage()));
            }
        }
        return List.copyOf(results);
    }

    private record BatchEntry(int index, VehicleEnvelope envelope) {
        private String eventId() {
            return envelope.getEventId();
        }

        private String vin() {
            return envelope.getVin();
        }
    }

    private static VehicleEnvelope parse(byte[] kafkaValue) {
        if (kafkaValue == null || kafkaValue.length == 0) {
            throw new IllegalArgumentException("VehicleEnvelope bytes must not be empty");
        }
        try {
            return VehicleEnvelope.parseFrom(kafkaValue);
        } catch (InvalidProtocolBufferException e) {
            throw new IllegalArgumentException("failed to parse VehicleEnvelope", e);
        }
    }

    private void writeTdengineFacts(List<VehicleEnvelope> envelopes) throws IOException {
        var rawFrames = envelopes.stream()
                .flatMap(envelope -> TdengineEnvelopeRows.rawFrame(envelope).stream())
                .toList();
        if (!rawFrames.isEmpty()) {
            tdengineWriter.appendRawFrames(rawFrames);
        }
        var locations = envelopes.stream()
                .flatMap(envelope -> TdengineEnvelopeRows.location(envelope).stream())
                .toList();
        if (!locations.isEmpty()) {
            tdengineWriter.appendLocations(locations);
        }
    }
}
