package com.lingniu.ingest.api.event;

import java.util.Map;
import java.util.Optional;

/**
 * Converts current {@link VehicleEvent} variants into normalized full-field
 * telemetry snapshots.
 *
 * <p>Protocol-specific mappers can add richer fields later, but all downstream
 * modules should consume {@link TelemetrySnapshot} instead of protocol objects.
 */
public final class VehicleEventTelemetrySnapshotMapper {

    private VehicleEventTelemetrySnapshotMapper() {
    }

    public static Optional<TelemetrySnapshot> toSnapshot(VehicleEvent event) {
        if (event == null || event instanceof VehicleEvent.RawArchive) {
            return Optional.empty();
        }
        TelemetrySnapshot.Builder builder = TelemetrySnapshot.builder(
                        event.eventId(),
                        event.vin(),
                        event.source(),
                        eventType(event),
                        event.eventTime(),
                        event.ingestTime())
                .metadata(event.metadata())
                .rawArchiveUri(rawArchiveUri(event));

        switch (event) {
            case VehicleEvent.Realtime realtime -> addRealtimeFields(builder, realtime.payload());
            case VehicleEvent.Location location -> addLocationFields(builder, location.payload());
            case VehicleEvent.Alarm alarm -> addAlarmFields(builder, alarm.payload());
            case VehicleEvent.Login login -> {
                addString(builder, "iccid", login.iccid(), "", "event.login.iccid");
                addString(builder, "protocol_version", login.protocolVersion(), "", "event.login.protocolVersion");
            }
            case VehicleEvent.Logout ignored -> {
            }
            case VehicleEvent.Heartbeat ignored -> {
            }
            case VehicleEvent.MediaMeta media -> {
                addString(builder, "media_id", media.mediaId(), "", "event.media.mediaId");
                addString(builder, "media_type", media.mediaType(), "", "event.media.mediaType");
                builder.addField(TelemetryFieldValue.longValue(
                        "media_size_bytes", media.sizeBytes(), "bytes", "event.media.sizeBytes"));
                addString(builder, "media_archive_ref", media.archiveRef(), "", "event.media.archiveRef");
            }
            case VehicleEvent.Passthrough passthrough -> {
                builder.addField(TelemetryFieldValue.longValue(
                        "passthrough_type", passthrough.passthroughType(), "", "event.passthrough.type"));
                builder.addField(TelemetryFieldValue.longValue(
                        "passthrough_size_bytes",
                        passthrough.data() == null ? 0 : passthrough.data().length,
                        "bytes",
                        "event.passthrough.data"));
            }
            case VehicleEvent.RawArchive ignored -> {
                return Optional.empty();
            }
        }
        return Optional.of(builder.build());
    }

    private static void addRealtimeFields(TelemetrySnapshot.Builder builder, RealtimePayload p) {
        if (p == null) return;
        addDouble(builder, "speed_kmh", p.speedKmh(), "km/h", "event.realtime.speedKmh");
        addDouble(builder, "total_mileage_km", p.totalMileageKm(), "km", "event.realtime.totalMileageKm");
        addDouble(builder, "battery_soc", p.batterySoc(), "%", "event.realtime.batterySoc");
        addDouble(builder, "battery_voltage_v", p.batteryVoltageV(), "V", "event.realtime.batteryVoltageV");
        addDouble(builder, "battery_current_a", p.batteryCurrentA(), "A", "event.realtime.batteryCurrentA");
        addDouble(builder, "fc_voltage_v", p.fcVoltageV(), "V", "event.realtime.fcVoltageV");
        addDouble(builder, "fc_current_a", p.fcCurrentA(), "A", "event.realtime.fcCurrentA");
        addDouble(builder, "fc_temp_c", p.fcTempC(), "C", "event.realtime.fcTempC");
        addDouble(builder, "hydrogen_remaining_kg", p.hydrogenRemainingKg(), "kg", "event.realtime.hydrogenRemainingKg");
        addDouble(builder, "hydrogen_high_pressure_mpa", p.hydrogenHighPressureMpa(), "MPa", "event.realtime.hydrogenHighPressureMpa");
        addDouble(builder, "hydrogen_low_pressure_mpa", p.hydrogenLowPressureMpa(), "MPa", "event.realtime.hydrogenLowPressureMpa");
        addEnum(builder, "vehicle_state", p.vehicleState(), "", "event.realtime.vehicleState");
        addEnum(builder, "charging_state", p.chargingState(), "", "event.realtime.chargingState");
        addEnum(builder, "running_mode", p.runningMode(), "", "event.realtime.runningMode");
        addLong(builder, "gear_level", p.gearLevel(), "", "event.realtime.gearLevel");
        addDouble(builder, "accelerator_pedal", p.acceleratorPedal(), "%", "event.realtime.acceleratorPedal");
        addDouble(builder, "brake_pedal", p.brakePedal(), "%", "event.realtime.brakePedal");
        addDouble(builder, "longitude", p.longitude(), "deg", "event.realtime.longitude");
        addDouble(builder, "latitude", p.latitude(), "deg", "event.realtime.latitude");
        addDouble(builder, "altitude_m", p.altitudeM(), "m", "event.realtime.altitudeM");
        addDouble(builder, "direction_deg", p.directionDeg(), "deg", "event.realtime.directionDeg");
        addDouble(builder, "ambient_temp_c", p.ambientTempC(), "C", "event.realtime.ambientTempC");
        addDouble(builder, "coolant_temp_c", p.coolantTempC(), "C", "event.realtime.coolantTempC");
    }

    private static void addLocationFields(TelemetrySnapshot.Builder builder, LocationPayload p) {
        if (p == null) return;
        builder.addField(TelemetryFieldValue.doubleValue("longitude", p.longitude(), "deg", "event.location.longitude"));
        builder.addField(TelemetryFieldValue.doubleValue("latitude", p.latitude(), "deg", "event.location.latitude"));
        builder.addField(TelemetryFieldValue.doubleValue("altitude_m", p.altitudeM(), "m", "event.location.altitudeM"));
        builder.addField(TelemetryFieldValue.doubleValue("speed_kmh", p.speedKmh(), "km/h", "event.location.speedKmh"));
        addDouble(builder, "total_mileage_km", p.totalMileageKm(), "km", "event.location.totalMileageKm");
        builder.addField(TelemetryFieldValue.doubleValue("direction_deg", p.directionDeg(), "deg", "event.location.directionDeg"));
        builder.addField(TelemetryFieldValue.longValue("location_alarm_flag", p.alarmFlag(), "", "event.location.alarmFlag"));
        builder.addField(TelemetryFieldValue.longValue("location_status_raw", p.statusFlag(), "", "event.location.statusFlag"));
    }

    private static void addAlarmFields(TelemetrySnapshot.Builder builder, AlarmPayload p) {
        if (p == null) return;
        addEnum(builder, "alarm_level", p.level(), "", "event.alarm.level");
        builder.addField(TelemetryFieldValue.longValue("alarm_type_code", p.alarmTypeCode(), "", "event.alarm.alarmTypeCode"));
        addString(builder, "alarm_type_name", p.alarmTypeName(), "", "event.alarm.alarmTypeName");
        if (p.faultCodes() != null && !p.faultCodes().isEmpty()) {
            builder.addField(TelemetryFieldValue.stringValue(
                    "fault_codes", String.join(",", p.faultCodes()), "", "event.alarm.faultCodes"));
        }
        if (p.activeBits() != null && !p.activeBits().isEmpty()) {
            builder.addField(TelemetryFieldValue.stringValue(
                    "active_alarm_bits", String.join(",", p.activeBits()), "", "event.alarm.activeBits"));
        }
        addDouble(builder, "longitude", p.longitude(), "deg", "event.alarm.longitude");
        addDouble(builder, "latitude", p.latitude(), "deg", "event.alarm.latitude");
        addEnum(builder, "safety_category", p.safetyCategory(), "", "event.alarm.safetyCategory");
        builder.addField(TelemetryFieldValue.booleanValue(
                "hydrogen_leak_detected", p.hydrogenLeakDetected(), "", "event.alarm.hydrogenLeakDetected"));
        addEnum(builder, "hydrogen_leak_level", p.hydrogenLeakLevel(), "", "event.alarm.hydrogenLeakLevel");
        builder.addField(TelemetryFieldValue.booleanValue(
                "hydrogen_leak_action_required",
                p.hydrogenLeakActionRequired(),
                "",
                "event.alarm.hydrogenLeakActionRequired"));
    }

    private static String eventType(VehicleEvent event) {
        if (event instanceof VehicleEvent.Realtime) return "REALTIME";
        if (event instanceof VehicleEvent.Location) return "LOCATION";
        if (event instanceof VehicleEvent.Alarm) return "ALARM";
        if (event instanceof VehicleEvent.Login) return "LOGIN";
        if (event instanceof VehicleEvent.Logout) return "LOGOUT";
        if (event instanceof VehicleEvent.Heartbeat) return "HEARTBEAT";
        if (event instanceof VehicleEvent.MediaMeta) return "MEDIA_META";
        if (event instanceof VehicleEvent.Passthrough) return "PASSTHROUGH";
        return "UNKNOWN";
    }

    private static String rawArchiveUri(VehicleEvent event) {
        Map<String, String> metadata = event.metadata();
        if (metadata == null) {
            return mediaArchiveRef(event);
        }
        String uri = metadata.getOrDefault(RawArchiveKeys.META_URI, "");
        if (uri != null && !uri.isBlank()) {
            return uri;
        }
        String key = metadata.getOrDefault(RawArchiveKeys.META_KEY, "");
        if (key != null && !key.isBlank()) {
            return RawArchiveKeys.logicalUri(key);
        }
        return mediaArchiveRef(event);
    }

    private static String mediaArchiveRef(VehicleEvent event) {
        if (event instanceof VehicleEvent.MediaMeta media && media.archiveRef() != null) {
            return media.archiveRef();
        }
        return "";
    }

    private static void addDouble(TelemetrySnapshot.Builder builder,
                                  String key,
                                  Double value,
                                  String unit,
                                  String sourcePath) {
        if (value != null) {
            builder.addField(TelemetryFieldValue.doubleValue(key, value, unit, sourcePath));
        }
    }

    private static void addLong(TelemetrySnapshot.Builder builder,
                                String key,
                                Integer value,
                                String unit,
                                String sourcePath) {
        if (value != null) {
            builder.addField(TelemetryFieldValue.longValue(key, value, unit, sourcePath));
        }
    }

    private static void addString(TelemetrySnapshot.Builder builder,
                                  String key,
                                  String value,
                                  String unit,
                                  String sourcePath) {
        if (value != null && !value.isBlank()) {
            builder.addField(TelemetryFieldValue.stringValue(key, value, unit, sourcePath));
        }
    }

    private static void addEnum(TelemetrySnapshot.Builder builder,
                                String key,
                                Enum<?> value,
                                String unit,
                                String sourcePath) {
        if (value != null) {
            builder.addField(TelemetryFieldValue.stringValue(key, value.name(), unit, sourcePath));
        }
    }
}
