package telemetry

import (
	"testing"

	"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
)

func TestLocationProjectionForProtocolUsesProtocolFields(t *testing.T) {
	tests := []struct {
		name     string
		protocol envelope.Protocol
		fields   map[string]any
		assert   func(*testing.T, LocationProjection)
	}{
		{
			name:     "gb32960",
			protocol: envelope.ProtocolGB32960,
			fields: map[string]any{
				"gb32960.position.latitude":     "30.56",
				"gb32960.position.longitude":    "121.0",
				"gb32960.vehicle.speed_kmh":     "54.3",
				"gb32960.vehicle.soc_percent":   "85",
				"gb32960.vehicle.total_mileage": "123",
			},
			assert: func(t *testing.T, location LocationProjection) {
				assertFloatPointer(t, location.SpeedKMH, 54.3)
				assertFloatPointer(t, location.SOCPercent, 85)
			},
		},
		{
			name:     "jt808",
			protocol: envelope.ProtocolJT808,
			fields: map[string]any{
				"jt808.location.latitude":      "30.590151",
				"jt808.location.longitude":     "121.069881",
				"jt808.location.speed_kmh":     "23",
				"jt808.location.altitude_m":    "5",
				"jt808.location.direction_deg": "79",
				"jt808.location.alarm_flag":    "2",
				"jt808.location.status_flag":   "786435",
			},
			assert: func(t *testing.T, location LocationProjection) {
				assertFloatPointer(t, location.SpeedKMH, 23)
				assertFloatPointer(t, location.AltitudeM, 5)
				assertFloatPointer(t, location.DirectionDeg, 79)
				assertIntPointer(t, location.AlarmFlag, 2)
				assertIntPointer(t, location.StatusFlag, 786435)
			},
		},
		{
			name:     "yutong root fallback",
			protocol: envelope.ProtocolYutongMQTT,
			fields: map[string]any{
				"yutong_mqtt.root.data.latitude":             "30.590921",
				"yutong_mqtt.root.data.longitude":            "121.075044",
				"yutong_mqtt.root.data.meter_speed":          "27",
				"yutong_mqtt.root.data.battery_capacity_soc": "78.4",
				"yutong_mqtt.root.data.gpsdirection":         "88",
			},
			assert: func(t *testing.T, location LocationProjection) {
				assertFloatPointer(t, location.SpeedKMH, 27)
				assertFloatPointer(t, location.SOCPercent, 78.4)
				assertFloatPointer(t, location.DirectionDeg, 88)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			location, ok := LocationProjectionForProtocol(tt.protocol, tt.fields)
			if !ok {
				t.Fatal("LocationProjectionForProtocol() did not find coordinates")
			}
			if location.Latitude == 0 || location.Longitude == 0 {
				t.Fatalf("coordinates = %v,%v", location.Latitude, location.Longitude)
			}
			tt.assert(t, location)
		})
	}
}

func TestLocationProjectionForProtocolRejectsBareStandardizedFields(t *testing.T) {
	for _, protocol := range []envelope.Protocol{
		envelope.ProtocolGB32960,
		envelope.ProtocolJT808,
		envelope.ProtocolYutongMQTT,
	} {
		if location, ok := LocationProjectionForProtocol(protocol, map[string]any{
			"latitude":  30.5,
			"longitude": 121.0,
			"speed_kmh": 20,
		}); ok {
			t.Fatalf("protocol %s unexpectedly accepted bare fields: %#v", protocol, location)
		}
	}
}

func TestHasRealtimeFieldsExcludesMetadataOnlyPayloads(t *testing.T) {
	tests := []struct {
		protocol envelope.Protocol
		fields   map[string]any
		want     bool
	}{
		{protocol: envelope.ProtocolGB32960, fields: map[string]any{"gb32960.header.command": "0x02"}, want: false},
		{protocol: envelope.ProtocolGB32960, fields: map[string]any{"gb32960.gd_fc_stack.stack_count": "1"}, want: true},
		{protocol: envelope.ProtocolJT808, fields: map[string]any{"jt808.registration.plate": "粤A00001"}, want: false},
		{protocol: envelope.ProtocolJT808, fields: map[string]any{"jt808.location.speed_kmh": "20"}, want: true},
		{protocol: envelope.ProtocolYutongMQTT, fields: map[string]any{"yutong_mqtt.metadata.topic": "/ytforward/shln/3"}, want: false},
		{protocol: envelope.ProtocolYutongMQTT, fields: map[string]any{"yutong_mqtt.data.meter_speed": "20"}, want: true},
	}
	for _, tt := range tests {
		if got := HasRealtimeFields(tt.protocol, tt.fields); got != tt.want {
			t.Fatalf("HasRealtimeFields(%s, %#v) = %v, want %v", tt.protocol, tt.fields, got, tt.want)
		}
	}
}

func assertFloatPointer(t *testing.T, value *float64, want float64) {
	t.Helper()
	if value == nil || *value != want {
		t.Fatalf("float pointer = %v, want %v", value, want)
	}
}

func assertIntPointer(t *testing.T, value *int64, want int64) {
	t.Helper()
	if value == nil || *value != want {
		t.Fatalf("int pointer = %v, want %v", value, want)
	}
}
