package com.lingniu.ingest.vehiclestate;

import com.lingniu.ingest.sink.kafka.proto.TelemetryField;
import com.lingniu.ingest.sink.kafka.proto.VehicleEnvelope;

import java.util.LinkedHashMap;
import java.util.Map;

public final class VehicleStateUpdater {

    private final VehicleStateRepository repository;

    public VehicleStateUpdater(VehicleStateRepository repository) {
        if (repository == null) {
            throw new IllegalArgumentException("repository must not be null");
        }
        this.repository = repository;
    }

    public void update(VehicleEnvelope envelope) {
        if (envelope == null) {
            throw new IllegalArgumentException("envelope must not be null");
        }
        if (!envelope.hasTelemetrySnapshot()) {
            throw new IllegalArgumentException("envelope telemetry_snapshot is required");
        }
        Map<String, String> fields = fields(envelope);
        String vin = envelope.getVin();

        // vehicle-state 只维护“最新状态”缓存，覆盖写 Redis，不承担历史查询或 RAW 回放职责。
        repository.putState(vin, json(base(envelope, fields)));
        repository.putLastEvent(vin, json(lastEvent(envelope)));

        if (fields.containsKey("longitude") && fields.containsKey("latitude")) {
            repository.putLocation(vin, json(location(envelope, fields)));
        }
        if (hasSafetyFields(fields)) {
            repository.putSafety(vin, json(safety(envelope, fields)));
        }
    }

    private static Map<String, String> fields(VehicleEnvelope envelope) {
        Map<String, String> fields = new LinkedHashMap<>();
        for (TelemetryField field : envelope.getTelemetrySnapshot().getFieldsList()) {
            // 同名字段以后到者为准，和最新状态语义一致。
            fields.put(field.getKey(), field.getValue());
        }
        return fields;
    }

    private static Map<String, Object> base(VehicleEnvelope envelope, Map<String, String> fields) {
        Map<String, Object> map = eventIdentity(envelope);
        map.put("eventType", envelope.getTelemetrySnapshot().getEventType());
        map.put("fields", fields);
        return map;
    }

    private static Map<String, Object> location(VehicleEnvelope envelope, Map<String, String> fields) {
        Map<String, Object> map = eventIdentity(envelope);
        putIfPresent(map, fields, "longitude");
        putIfPresent(map, fields, "latitude");
        putIfPresent(map, fields, "altitude_m");
        putIfPresent(map, fields, "direction_deg");
        putIfPresent(map, fields, "speed_kmh");
        return map;
    }

    private static Map<String, Object> safety(VehicleEnvelope envelope, Map<String, String> fields) {
        Map<String, Object> map = eventIdentity(envelope);
        putIfPresent(map, fields, "safety_category");
        putIfPresent(map, fields, "hydrogen_leak_detected");
        putIfPresent(map, fields, "hydrogen_leak_level");
        putIfPresent(map, fields, "hydrogen_leak_action_required");
        putIfPresent(map, fields, "alarm_level");
        return map;
    }

    private static Map<String, Object> lastEvent(VehicleEnvelope envelope) {
        return eventIdentity(envelope);
    }

    private static Map<String, Object> eventIdentity(VehicleEnvelope envelope) {
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("eventId", envelope.getEventId());
        map.put("traceId", envelope.getTraceId());
        map.put("vin", envelope.getVin());
        map.put("source", envelope.getSource());
        map.put("eventTimeMs", Long.toString(envelope.getEventTimeMs()));
        map.put("ingestTimeMs", Long.toString(envelope.getIngestTimeMs()));
        return map;
    }

    private static boolean hasSafetyFields(Map<String, String> fields) {
        // safety 是从标准字段中筛出来的窄视图，字段缺失时不写对应 Redis key。
        return fields.containsKey("safety_category")
                || fields.containsKey("hydrogen_leak_detected")
                || fields.containsKey("hydrogen_leak_level")
                || fields.containsKey("hydrogen_leak_action_required")
                || fields.containsKey("alarm_level");
    }

    private static void putIfPresent(Map<String, Object> map, Map<String, String> fields, String key) {
        if (fields.containsKey(key)) {
            map.put(key, fields.get(key));
        }
    }

    private static String json(Map<String, ?> map) {
        StringBuilder out = new StringBuilder("{");
        boolean first = true;
        for (Map.Entry<String, ?> entry : map.entrySet()) {
            if (!first) {
                out.append(',');
            }
            first = false;
            out.append('"').append(escape(entry.getKey())).append('"').append(':');
            Object value = entry.getValue();
            if (value instanceof Map<?, ?> nested) {
                out.append(jsonObject(nested));
            } else {
                out.append('"').append(escape(String.valueOf(value))).append('"');
            }
        }
        return out.append('}').toString();
    }

    private static String jsonObject(Map<?, ?> map) {
        StringBuilder out = new StringBuilder("{");
        boolean first = true;
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            if (!first) {
                out.append(',');
            }
            first = false;
            out.append('"').append(escape(String.valueOf(entry.getKey()))).append('"')
                    .append(':')
                    .append('"').append(escape(String.valueOf(entry.getValue()))).append('"');
        }
        return out.append('}').toString();
    }

    private static String escape(String value) {
        return value.replace("\\", "\\\\").replace("\"", "\\\"");
    }
}
