package yutongmqtt

import (
	"errors"
	"testing"

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

func TestParseMessageMapsYutongPayloadToEnvelope(t *testing.T) {
	payload := []byte(`{
	  "device": "LTEST000000000001",
	  "time": "20260413100000",
	  "plateNo": "豫A12345",
	  "data": {
	    "METER_SPEED": 52.3,
	    "TOTAL_MILEAGE": 123456.7,
	    "BATTERY_CAPACITY_SOC": 70,
	    "LONGITUDE": 116397128,
	    "LATITUDE": 39916527,
	    "GPSDirection": 88
	  }
	}`)

	env, err := ParseMessage("endpoint-a", "/ytforward/shln/dev1", payload, 1782745114999)
	if err != nil {
		t.Fatalf("ParseMessage() error = %v", err)
	}
	if env.Protocol != envelope.ProtocolYutongMQTT {
		t.Fatalf("protocol = %q", env.Protocol)
	}
	if env.VIN != "LTEST000000000001" || env.DeviceID != "LTEST000000000001" {
		t.Fatalf("identity fields = vin:%q device:%q", env.VIN, env.DeviceID)
	}
	if env.Plate != "豫A12345" {
		t.Fatalf("plate = %q", env.Plate)
	}
	assertFloatField(t, env, envelope.FieldSpeedKMH, 52.3)
	assertFloatField(t, env, envelope.FieldTotalMileageKM, 123.4567)
	assertFloatField(t, env, envelope.FieldSOCPercent, 70)
	assertFloatField(t, env, envelope.FieldLongitude, 116.397128)
	assertFloatField(t, env, envelope.FieldLatitude, 39.916527)
	assertFloatField(t, env, "direction_deg", 88)
	if env.EventTimeMS == 1782745114999 {
		t.Fatal("event time should come from device time")
	}
	if env.RawText == "" || env.Parsed["data"] == nil {
		t.Fatalf("raw/parsed missing: %#v", env)
	}
	if env.SourceCode != "endpoint-a" || env.PlatformName != "endpoint-a" || env.SourceKind != "PLATFORM" {
		t.Fatalf("source metadata = code:%q platform:%q kind:%q", env.SourceCode, env.PlatformName, env.SourceKind)
	}
}

func TestParseMessageUsesYutongEndpointDisplayName(t *testing.T) {
	payload := []byte(`{"device":"LMRKH9AC3R1004101","time":"20260413100000","data":{"TOTAL_MILEAGE":56905000}}`)
	env, err := ParseMessage("yutong", "/ytforward/shln/1", payload, 1782745114999)
	if err != nil {
		t.Fatalf("ParseMessage() error = %v", err)
	}
	if env.SourceCode != "yutong" || env.PlatformName != "宇通" || env.SourceKind != "PLATFORM" {
		t.Fatalf("source metadata = code:%q platform:%q kind:%q", env.SourceCode, env.PlatformName, env.SourceKind)
	}
}

func TestParseMessageUsesDeviceAsVehicleKeyWhenNotVIN(t *testing.T) {
	payload := []byte(`{"device":"YT-DEVICE-001","time":"20260413100000","data":{"METER_SPEED":"12.3"}}`)
	env, err := ParseMessage("endpoint-a", "/ytforward/shln/YT-DEVICE-001", payload, 1782745114999)
	if err != nil {
		t.Fatalf("ParseMessage() error = %v", err)
	}
	if env.VIN != "" {
		t.Fatalf("non-vin device should not be treated as vin: %q", env.VIN)
	}
	if env.VehicleKey() != "YT-DEVICE-001" {
		t.Fatalf("vehicle key = %q", env.VehicleKey())
	}
	assertFloatField(t, env, envelope.FieldSpeedKMH, 12.3)
}

func TestParseMessageNormalizesProductionMileageMeters(t *testing.T) {
	payload := []byte(`{
	  "device":"LMRKH9AC3R1004101",
	  "time":"2026-07-02 01:02:04.085",
	  "data":{"TOTAL_MILEAGE":56905000,"METER_SPEED":19}
	}`)
	env, err := ParseMessage("yutong", "/ytforward/shln/1", payload, 1782940000000)
	if err != nil {
		t.Fatalf("ParseMessage() error = %v", err)
	}
	assertFloatField(t, env, envelope.FieldTotalMileageKM, 56905)
	if env.EventTimeMS != 1782925324085 {
		t.Fatalf("event time = %d, want 1782925324085", env.EventTimeMS)
	}
}

func TestParseMessageTreatsSmallYutongMileageAsMeters(t *testing.T) {
	payload := []byte(`{
	  "device":"LMRKH9AC3R1004101",
	  "time":"2026-07-02 01:02:04.085",
	  "data":{"TOTAL_MILEAGE":1024,"METER_SPEED":19}
	}`)
	env, err := ParseMessage("yutong", "/ytforward/shln/1", payload, 1782940000000)
	if err != nil {
		t.Fatalf("ParseMessage() error = %v", err)
	}
	assertFloatField(t, env, envelope.FieldTotalMileageKM, 1.024)
}

func TestParseMessageRejectsMalformedJSON(t *testing.T) {
	_, err := ParseMessage("endpoint-a", "/bad", []byte("{bad-json"), 1782745114999)
	if !errors.Is(err, ErrInvalidJSON) {
		t.Fatalf("error = %v", err)
	}
}

func assertFloatField(t *testing.T, env envelope.FrameEnvelope, key string, want float64) {
	t.Helper()
	got, ok := env.Fields[key].(float64)
	if !ok {
		t.Fatalf("field %s missing or not float64: %#v", key, env.Fields[key])
	}
	if got != want {
		t.Fatalf("field %s = %v, want %v", key, got, want)
	}
}
