package stats

import (
	"context"
	"database/sql"
	"database/sql/driver"
	"errors"
	"fmt"
	"math"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/DATA-DOG/go-sqlmock"
	"lingniu-vehicle-ingest/go/vehicle-gateway/internal/envelope"
)

func managedTestSource(env envelope.FrameEnvelope) envelope.FrameEnvelope {
	if strings.TrimSpace(env.SourceKind) == "" {
		env.SourceKind = "PLATFORM"
	}
	return env
}

func TestSamplesFromEnvelopeDerivesDailyMileageSample(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	env := envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		VIN:         "LNBVIN00000000001",
		EventTimeMS: time.Date(2026, 7, 1, 8, 30, 0, 0, loc).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 10241.2,
		},
	}

	samples, err := SamplesFromEnvelope(env, loc)
	if err != nil {
		t.Fatalf("SamplesFromEnvelope() error = %v", err)
	}
	if len(samples) != 1 {
		t.Fatalf("sample count = %d", len(samples))
	}
	if samples[0].VIN != "LNBVIN00000000001" {
		t.Fatalf("vin = %q", samples[0].VIN)
	}
	if samples[0].TotalMileageKM != 10241.2 {
		t.Fatalf("total mileage = %v", samples[0].TotalMileageKM)
	}
	if samples[0].StatDate != "2026-07-01" {
		t.Fatalf("stat date = %q", samples[0].StatDate)
	}
}

func TestSamplesFromEnvelopeMapsProtocolMileageFields(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	tests := []struct {
		name     string
		protocol envelope.Protocol
		fields   map[string]any
		wantKM   float64
	}{
		{
			name:     "gb32960",
			protocol: envelope.ProtocolGB32960,
			fields:   map[string]any{"gb32960.vehicle.total_mileage_km": "38587"},
			wantKM:   38587,
		},
		{
			name:     "jt808",
			protocol: envelope.ProtocolJT808,
			fields:   map[string]any{"jt808.location.total_mileage_km": "12432.4"},
			wantKM:   12432.4,
		},
		{
			name:     "yutong mqtt meters",
			protocol: envelope.ProtocolYutongMQTT,
			fields:   map[string]any{"yutong_mqtt.data.total_mileage": "120756000"},
			wantKM:   120756,
		},
		{
			name:     "yutong mqtt km",
			protocol: envelope.ProtocolYutongMQTT,
			fields:   map[string]any{"yutong_mqtt.data.total_mileage_km": "120756"},
			wantKM:   120756,
		},
		{
			name:     "yutong mqtt root meters",
			protocol: envelope.ProtocolYutongMQTT,
			fields:   map[string]any{"yutong_mqtt.root.data.total_mileage": float64(22858000)},
			wantKM:   22858,
		},
		{
			name:     "yutong mqtt root km",
			protocol: envelope.ProtocolYutongMQTT,
			fields:   map[string]any{"yutong_mqtt.root.data.total_mileage_km": float64(22858)},
			wantKM:   22858,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
				Protocol:    tt.protocol,
				VIN:         "LNBVIN00000000001",
				EventTimeMS: time.Date(2026, 7, 1, 8, 30, 0, 0, loc).UnixMilli(),
				Fields:      tt.fields,
			}, loc)
			if err != nil {
				t.Fatalf("SamplesFromEnvelope() error = %v", err)
			}
			if len(samples) != 1 {
				t.Fatalf("sample count = %d", len(samples))
			}
			if samples[0].TotalMileageKM != tt.wantKM {
				t.Fatalf("total mileage = %v, want %v", samples[0].TotalMileageKM, tt.wantKM)
			}
		})
	}
}

func TestSamplesFromEnvelopeFallsBackToReceivedTimeWhenEventTimeIsFuture(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	received := time.Date(2026, 7, 8, 23, 59, 0, 0, loc)
	futureEvent := received.Add(48 * time.Hour)
	samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
		Protocol:     envelope.ProtocolJT808,
		VIN:          "LA9GG64L7PBAF4001",
		EventTimeMS:  futureEvent.UnixMilli(),
		ReceivedAtMS: received.UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4136.8,
		},
	}, loc)
	if err != nil {
		t.Fatalf("SamplesFromEnvelope() error = %v", err)
	}
	if len(samples) != 1 {
		t.Fatalf("sample count = %d", len(samples))
	}
	if samples[0].StatDate != "2026-07-08" {
		t.Fatalf("stat date = %q, want received date", samples[0].StatDate)
	}
	if !samples[0].EventTime.Equal(received) {
		t.Fatalf("event time = %s, want received time %s", samples[0].EventTime, received)
	}
	_, result, err := samplesFromEnvelopeWithResult(envelope.FrameEnvelope{
		Protocol:     envelope.ProtocolJT808,
		VIN:          "LA9GG64L7PBAF4001",
		EventTimeMS:  futureEvent.UnixMilli(),
		ReceivedAtMS: received.UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4136.8,
		},
	}, loc)
	if err != nil {
		t.Fatalf("samplesFromEnvelopeWithResult() error = %v", err)
	}
	if result.SamplesAdjustedFutureEventTime != 1 {
		t.Fatalf("future event adjustment count = %d, want 1", result.SamplesAdjustedFutureEventTime)
	}
}

func TestSamplesFromEnvelopeKeepsSmallFutureClockSkew(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	received := time.Date(2026, 7, 8, 23, 59, 0, 0, loc)
	eventTime := received.Add(2 * time.Minute)
	samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
		Protocol:     envelope.ProtocolJT808,
		VIN:          "LA9GG64L7PBAF4001",
		EventTimeMS:  eventTime.UnixMilli(),
		ReceivedAtMS: received.UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4136.8,
		},
	}, loc)
	if err != nil {
		t.Fatalf("SamplesFromEnvelope() error = %v", err)
	}
	if len(samples) != 1 {
		t.Fatalf("sample count = %d", len(samples))
	}
	if samples[0].StatDate != "2026-07-09" {
		t.Fatalf("stat date = %q, want event date within skew", samples[0].StatDate)
	}
	if !samples[0].EventTime.Equal(eventTime) {
		t.Fatalf("event time = %s, want event time %s", samples[0].EventTime, eventTime)
	}
}

func TestWriterSourceSeenAtUsesReceivedTimeOverDeviceEventTime(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(&recordingExec{}, loc)
	received := time.Date(2026, 7, 8, 13, 20, 0, 0, loc)
	staleDeviceEvent := received.Add(-72 * time.Hour)

	got := writer.sourceSeenAt(envelope.FrameEnvelope{
		EventTimeMS:  staleDeviceEvent.UnixMilli(),
		ReceivedAtMS: received.UnixMilli(),
	})
	if !got.Equal(received) {
		t.Fatalf("sourceSeenAt = %s, want received time %s", got, received)
	}
}

func TestSamplesFromEnvelopeSkipsMissingVIN(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		Phone:       "13307811254",
		EventTimeMS: time.Date(2026, 7, 2, 0, 3, 0, 0, loc).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 10985.7,
		},
	}, loc)
	if err != nil {
		t.Fatalf("SamplesFromEnvelope() error = %v", err)
	}
	if len(samples) != 0 {
		t.Fatalf("expected no samples without VIN, got %#v", samples)
	}
}

func TestSamplesFromEnvelopeSkipsMissingVINOrMileage(t *testing.T) {
	samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
		Protocol: envelope.ProtocolJT808,
		Fields:   map[string]any{"jt808.location.total_mileage_km": 1.2},
	}, nil)
	if err != nil {
		t.Fatalf("SamplesFromEnvelope() error = %v", err)
	}
	if len(samples) != 0 {
		t.Fatalf("expected no samples without VIN, got %#v", samples)
	}

	samples, err = SamplesFromEnvelope(envelope.FrameEnvelope{
		Protocol: envelope.ProtocolGB32960,
		VIN:      "LNBVIN00000000002",
		Fields:   map[string]any{},
	}, nil)
	if err != nil {
		t.Fatalf("SamplesFromEnvelope() error = %v", err)
	}
	if len(samples) != 0 {
		t.Fatalf("expected no samples without mileage, got %#v", samples)
	}
}

func TestSamplesFromEnvelopeSkipsNonPositiveMileage(t *testing.T) {
	for _, value := range []any{0, 0.0, -1.0, "0"} {
		samples, err := SamplesFromEnvelope(envelope.FrameEnvelope{
			Protocol:    envelope.ProtocolJT808,
			VIN:         "LNBVIN00000000001",
			EventTimeMS: time.Date(2026, 7, 1, 9, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
			Fields: map[string]any{
				"jt808.location.total_mileage_km": value,
			},
		}, nil)
		if err != nil {
			t.Fatalf("SamplesFromEnvelope(%#v) error = %v", value, err)
		}
		if len(samples) != 0 {
			t.Fatalf("expected no samples for non-positive mileage %#v, got %#v", value, samples)
		}
	}
}

func TestWriterAppendWritesSourceCandidateAndProjection(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		SourceCode:     "g7s",
		PlatformName:   "G7s",
		SourceKind:     "PLATFORM",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	}

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("Append() error = %v", err)
	}
	if len(exec.calls) != 7 {
		t.Fatalf("exec calls = %d, want source + candidate + normalize insert/delete + project + reconcile + cleanup", len(exec.calls))
	}
	if !strings.Contains(exec.calls[0].query, "INSERT INTO vehicle_data_source") {
		t.Fatalf("first call should upsert source: %s", exec.calls[0].query)
	}
	if exec.calls[0].args[3] != "G7s" || exec.calls[0].args[4] != "g7s" || exec.calls[0].args[5] != "PLATFORM" {
		t.Fatalf("source metadata args = %#v", exec.calls[0].args)
	}
	if !strings.Contains(exec.calls[1].query, "INSERT INTO vehicle_daily_mileage_source") {
		t.Fatalf("second call should upsert candidate: %s", exec.calls[1].query)
	}
	if exec.calls[1].args[8] != "G7s" {
		t.Fatalf("source mileage platform arg = %#v", exec.calls[1].args)
	}
	if !strings.Contains(exec.calls[2].query, "INSERT INTO vehicle_daily_mileage_source") ||
		!strings.Contains(exec.calls[2].query, "SELECT") ||
		!strings.Contains(exec.calls[2].query, "@PLATFORM:") {
		t.Fatalf("third call should normalize legacy platform source rows: %s", exec.calls[2].query)
	}
	if !strings.Contains(exec.calls[3].query, "DELETE s") ||
		!strings.Contains(exec.calls[3].query, "@PLATFORM:") {
		t.Fatalf("fourth call should delete normalized legacy platform rows: %s", exec.calls[3].query)
	}
	if !strings.Contains(exec.calls[4].query, "INSERT INTO vehicle_daily_mileage") {
		t.Fatalf("fifth call should project final: %s", exec.calls[4].query)
	}
}

func TestWriterAppendWithResultReportsWrittenSample(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	}

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 1 || result.SamplesWritten != 1 {
		t.Fatalf("result = %+v, want one found and written sample", result)
	}
	if result.ProjectionsAttempted != 1 || result.ProjectionsWritten != 1 || result.ProjectionsSkippedThrottled != 0 {
		t.Fatalf("projection result = %+v, want one final projection", result)
	}
	if result.SourceTouchesSkippedUnmanaged != 1 || result.SourceTouchesAttempted != 0 || result.SourceTouchesWritten != 0 || result.SourceTouchesSkippedMissing != 0 {
		t.Fatalf("source touch result = %+v, want unmanaged source skipped", result)
	}
	if result.SamplesSkippedMissingVIN != 0 ||
		result.SamplesSkippedMissingMileage != 0 ||
		result.SamplesSkippedNonPositiveMileage != 0 ||
		result.SamplesSkippedSameMileage != 0 ||
		result.SamplesSkippedMissingSource != 0 {
		t.Fatalf("unexpected skipped result counters: %+v", result)
	}
}

func TestWriterAppendSkipsMileageWithoutSourceIdentity(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		VIN:         "LA9GG64L7PBAF4001",
		Phone:       "13307765812",
		EventTimeMS: time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	}

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("Append() error = %v", err)
	}
	if len(exec.calls) != 0 {
		t.Fatalf("exec calls = %d, want 0 without source identity", len(exec.calls))
	}
}

func TestWriterAppendWithResultReportsMissingSource(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		VIN:         "LA9GG64L7PBAF4001",
		Phone:       "13307765812",
		EventTimeMS: time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	}

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 1 || result.SamplesSkippedMissingSource != 1 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want found sample skipped by missing source", result)
	}
	if result.SourceTouchesSkippedMissing != 1 || result.SourceTouchesAttempted != 0 || result.SourceTouchesWritten != 0 {
		t.Fatalf("source touch result = %+v, want missing source endpoint", result)
	}
	if result.ProjectionsAttempted != 0 || result.ProjectionsWritten != 0 || result.ProjectionsSkippedThrottled != 0 {
		t.Fatalf("projection result = %+v, want no projection without source", result)
	}
	if len(exec.calls) != 0 {
		t.Fatalf("exec calls = %d, want 0 without source identity", len(exec.calls))
	}
}

func TestWriterAppendTracksSourceWithoutMileageSample(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		Phone:          "13307795518",
		SourceEndpoint: "222.66.200.68:43614",
		EventTimeMS:    time.Date(2026, 7, 8, 16, 30, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.speed_kmh": 38.5,
		},
	})

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("Append() error = %v", err)
	}
	if len(exec.calls) != 1 {
		t.Fatalf("exec calls = %d, want source only", len(exec.calls))
	}
	if !strings.Contains(exec.calls[0].query, "INSERT INTO vehicle_data_source") {
		t.Fatalf("source upsert query missing: %s", exec.calls[0].query)
	}
	if got := exec.calls[0].args[1]; got != "222.66.200.68" {
		t.Fatalf("source ip arg = %#v", got)
	}
}

func TestWriterAppendWithResultReportsMissingMileage(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.speed_kmh": 38.5,
		},
	})

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 0 || result.SamplesSkippedMissingMileage != 1 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want missing mileage", result)
	}
	if result.SourceTouchesAttempted != 1 || result.SourceTouchesWritten != 1 || result.SourceTouchesSkippedThrottled != 0 {
		t.Fatalf("source touch result = %+v, want source touch before missing mileage skip", result)
	}
	if result.ProjectionsAttempted != 0 || result.ProjectionsWritten != 0 || result.ProjectionsSkippedThrottled != 0 {
		t.Fatalf("projection result = %+v, want no projection for missing mileage", result)
	}
}

func TestWriterAppendWithResultClassifiesGB32960VendorFrameAsNonMileage(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolGB32960,
		VIN:            "LB9A32A24R0LS1426",
		SourceEndpoint: "115.29.187.205:32960",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"gb32960.gd_fc_stack.stack_water_outlet_temp_c":   63,
			"gb32960.gd_fc_stack.hydrogen_inlet_pressure_kpa": 130,
		},
	})

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 0 || result.SamplesSkippedNonMileageFrame != 1 || result.SamplesSkippedMissingMileage != 0 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want non-mileage frame", result)
	}
	if result.SourceTouchesAttempted != 1 || result.SourceTouchesWritten != 1 {
		t.Fatalf("source touch result = %+v, want source touch before non-mileage skip", result)
	}
}

func TestWriterAppendWithResultKeepsGB32960VehicleFrameMissingMileageActionable(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolGB32960,
		VIN:            "LB9A32A24R0LS1426",
		SourceEndpoint: "115.29.187.205:32960",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"gb32960.vehicle.speed_kmh":   38.5,
			"gb32960.vehicle.soc_percent": 81.2,
		},
	}

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 0 || result.SamplesSkippedMissingMileage != 1 || result.SamplesSkippedNonMileageFrame != 0 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want actionable missing mileage", result)
	}
}

func TestWriterAppendWithResultClassifiesYutongSparseFrameAsMissingMileage(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolYutongMQTT,
		VIN:            "LMRKH9AC2R1004087",
		SourceEndpoint: "mqtt://yutong/ytforward/shln/3",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"yutong_mqtt.data.latitude":  30.590921,
			"yutong_mqtt.data.longitude": 121.075044,
		},
	}

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 0 || result.SamplesSkippedMissingMileage != 1 || result.SamplesSkippedNonMileageFrame != 0 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want missing mileage", result)
	}
}

func TestWriterAppendWithResultClassifiesYutongMetadataOnlyFrameAsNonMileage(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolYutongMQTT,
		VIN:            "LMRKH9AC2R1004087",
		SourceEndpoint: "mqtt://yutong/ytforward/shln/3",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"yutong_mqtt.metadata.topic": "/ytforward/shln/3",
			"yutong_mqtt.root.device":    "LMRKH9AC2R1004087",
		},
	}

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 0 || result.SamplesSkippedNonMileageFrame != 1 || result.SamplesSkippedMissingMileage != 0 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want non-mileage frame", result)
	}
}

func TestWriterAppendWithResultReportsMissingTime(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesFound != 0 || result.SamplesSkippedMissingTime != 1 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want missing time skip", result)
	}
	if result.SourceTouchesAttempted != 1 || result.SourceTouchesWritten != 1 || result.SourceTouchesSkippedThrottled != 0 {
		t.Fatalf("source touch result = %+v, want source touch before missing time skip", result)
	}
	if result.ProjectionsAttempted != 0 || result.ProjectionsWritten != 0 || result.ProjectionsSkippedThrottled != 0 {
		t.Fatalf("projection result = %+v, want no projection for missing time", result)
	}
}

func TestWriterAppendWithResultReportsMissingFieldsAndDoesNotTouchSource(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Parsed: map[string]any{
			"location": map[string]any{"total_mileage_km": 4123.9},
		},
	}

	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("AppendWithResult() error = %v", err)
	}
	if result.SamplesSkippedMissingFields != 1 || result.SamplesSkippedMissingMileage != 0 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want missing fields only", result)
	}
	if result.SourceTouchesAttempted != 0 || result.SourceTouchesWritten != 0 || result.SourceTouchesSkippedMissing != 0 {
		t.Fatalf("source touch result = %+v, want no source touch without fields", result)
	}
	if len(exec.calls) != 0 {
		t.Fatalf("exec calls = %d, want 0 for missing fields", len(exec.calls))
	}
}

func TestSamplesFromEnvelopeUsesFieldsOnly(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	_, result, err := samplesFromEnvelopeWithResult(envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		VIN:         "LA9GG64L7PBAF4001",
		EventTimeMS: time.Date(2026, 7, 8, 13, 20, 0, 0, loc).UnixMilli(),
		Parsed: map[string]any{
			"location": map[string]any{"total_mileage_km": 4123.9},
		},
	}, loc)
	if err != nil {
		t.Fatalf("samplesFromEnvelopeWithResult() error = %v", err)
	}
	if result.SamplesSkippedMissingFields != 1 || result.SamplesFound != 0 {
		t.Fatalf("result = %+v, want missing fields and no extracted samples", result)
	}
}

func TestWriterAppendDedupesRealtimeMileagePerSource(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	base := managedTestSource(envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		VIN:         "LA9GG64L7PBAF4001",
		Phone:       "13307765812",
		EventTimeMS: time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	sourceA := base
	sourceA.SourceEndpoint = "115.231.168.135:20215"
	if err := writer.Append(context.Background(), sourceA); err != nil {
		t.Fatalf("Append(sourceA) error = %v", err)
	}

	sourceB := base
	sourceB.SourceEndpoint = "115.159.85.149:28316"
	sourceB.ReceivedAtMS = sourceB.EventTimeMS + 1000
	if err := writer.Append(context.Background(), sourceB); err != nil {
		t.Fatalf("Append(sourceB) error = %v", err)
	}

	sourceADuplicate := sourceA
	sourceADuplicate.ReceivedAtMS = sourceA.EventTimeMS + 2000
	if err := writer.Append(context.Background(), sourceADuplicate); err != nil {
		t.Fatalf("Append(sourceADuplicate) error = %v", err)
	}

	if got := countExecQueries(exec.calls, "INSERT INTO vehicle_daily_mileage_source"); got != 2 {
		t.Fatalf("candidate upserts = %d, want 2", got)
	}
	if got := countExecQueries(exec.calls, "INSERT INTO vehicle_data_source"); got != 2 {
		t.Fatalf("source upserts = %d, want 2", got)
	}
}

func TestWriterAppendCollapsesDirectSourceAcrossChangingIPs(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	base := envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		VIN:         "LA9GG64L7PBAF4001",
		Phone:       "13307765812",
		SourceKind:  "DIRECT",
		EventTimeMS: time.Date(2026, 7, 8, 13, 20, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	}

	first := base
	first.SourceEndpoint = "115.231.168.135:20215"
	if err := writer.Append(context.Background(), first); err != nil {
		t.Fatalf("Append(first) error = %v", err)
	}

	second := base
	second.SourceEndpoint = "39.144.3.22:60177"
	second.ReceivedAtMS = second.EventTimeMS + 1000
	if err := writer.Append(context.Background(), second); err != nil {
		t.Fatalf("Append(second) error = %v", err)
	}

	var sourceKeys []string
	for _, call := range exec.calls {
		if strings.Contains(call.query, "INSERT INTO vehicle_daily_mileage_source") && len(call.args) > 3 {
			sourceKeys = append(sourceKeys, call.args[3].(string))
		}
	}
	if len(sourceKeys) != 1 {
		t.Fatalf("source mileage upserts = %d, want one because identical mileage from same direct device should be deduplicated: %#v", len(sourceKeys), sourceKeys)
	}
	if sourceKeys[0] != "JT808:13307765812@DIRECT" {
		t.Fatalf("source key = %q", sourceKeys[0])
	}
}

func TestWriterAppendCollapsesPlatformSourceAcrossChangingIPs(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	base := envelope.FrameEnvelope{
		Protocol:     envelope.ProtocolJT808,
		VIN:          "LNXNEGRR0SR321372",
		Phone:        "41456413943",
		SourceCode:   "guangan_beidou",
		PlatformName: "广安北斗",
		SourceKind:   "PLATFORM",
		EventTimeMS:  time.Date(2026, 7, 12, 8, 40, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 46484.2,
		},
	}

	first := base
	first.SourceEndpoint = "117.132.194.167:9806"
	if err := writer.Append(context.Background(), first); err != nil {
		t.Fatalf("Append(first) error = %v", err)
	}

	second := base
	second.SourceEndpoint = "117.132.198.90:12084"
	second.EventTimeMS = second.EventTimeMS + 1000
	second.Fields = map[string]any{"jt808.location.total_mileage_km": 46484.3}
	if err := writer.Append(context.Background(), second); err != nil {
		t.Fatalf("Append(second) error = %v", err)
	}

	var sourceKeys []string
	for _, call := range exec.calls {
		if strings.Contains(call.query, "INSERT INTO vehicle_daily_mileage_source") && len(call.args) > 3 {
			sourceKeys = append(sourceKeys, call.args[3].(string))
		}
	}
	if len(sourceKeys) != 2 {
		t.Fatalf("source mileage upserts = %d, want both changing mileage samples written: %#v", len(sourceKeys), sourceKeys)
	}
	for _, sourceKey := range sourceKeys {
		if sourceKey != "JT808:41456413943@PLATFORM:guangan_beidou" {
			t.Fatalf("source key = %q", sourceKey)
		}
	}
	if got := countExecQueries(exec.calls, "INSERT INTO vehicle_data_source"); got != 2 {
		t.Fatalf("source touch upserts = %d, want both platform IPs still observed", got)
	}
}

func TestWriterAppendThrottlesDailyProjectionPerVehicleProtocolDate(t *testing.T) {
	exec := &recordingExec{}
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(exec, loc)
	base := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	first := base
	first.EventTimeMS = time.Date(2026, 7, 8, 13, 20, 0, 0, loc).UnixMilli()
	if err := writer.Append(context.Background(), first); err != nil {
		t.Fatalf("first Append() error = %v", err)
	}
	second := base
	second.EventTimeMS = time.Date(2026, 7, 8, 13, 20, 5, 0, loc).UnixMilli()
	second.Fields = map[string]any{"jt808.location.total_mileage_km": 4124.2}
	if err := writer.Append(context.Background(), second); err != nil {
		t.Fatalf("second Append() error = %v", err)
	}
	third := base
	third.EventTimeMS = time.Date(2026, 7, 8, 13, 20, 16, 0, loc).UnixMilli()
	third.Fields = map[string]any{"jt808.location.total_mileage_km": 4125.0}
	if err := writer.Append(context.Background(), third); err != nil {
		t.Fatalf("third Append() error = %v", err)
	}

	if got := countExecQueries(exec.calls, "INSERT INTO vehicle_daily_mileage_source"); got != 3 {
		t.Fatalf("source candidate upserts = %d, want 3", got)
	}
	if got := countExecQueries(exec.calls, "INSERT INTO vehicle_daily_mileage\n"); got != 2 {
		t.Fatalf("daily projections = %d, want 2", got)
	}
}

func TestWriterAppendProjectsImmediatelyForNewSource(t *testing.T) {
	exec := &recordingExec{}
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(exec, loc)
	base := managedTestSource(envelope.FrameEnvelope{
		Protocol:    envelope.ProtocolJT808,
		VIN:         "LA9GG64L7PBAF4001",
		Phone:       "13307765812",
		EventTimeMS: time.Date(2026, 7, 8, 13, 20, 0, 0, loc).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	sourceA := base
	sourceA.SourceEndpoint = "115.231.168.135:20215"
	if err := writer.Append(context.Background(), sourceA); err != nil {
		t.Fatalf("Append(sourceA) error = %v", err)
	}
	sourceB := base
	sourceB.SourceEndpoint = "115.159.85.149:28316"
	sourceB.Fields = map[string]any{"jt808.location.total_mileage_km": 4124.0}
	if err := writer.Append(context.Background(), sourceB); err != nil {
		t.Fatalf("Append(sourceB) error = %v", err)
	}

	if got := countExecQueries(exec.calls, "INSERT INTO vehicle_daily_mileage\n"); got != 2 {
		t.Fatalf("daily projections = %d, want immediate projection for each new source", got)
	}
}

func TestWriterProjectionIntervalZeroKeepsLegacyPerSampleProjection(t *testing.T) {
	exec := &recordingExec{}
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(exec, loc)
	writer.SetProjectionInterval(0)
	base := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	for i, mileage := range []float64{4123.9, 4124.0} {
		event := base
		event.EventTimeMS = time.Date(2026, 7, 8, 13, 20, i, 0, loc).UnixMilli()
		event.Fields = map[string]any{"jt808.location.total_mileage_km": mileage}
		if err := writer.Append(context.Background(), event); err != nil {
			t.Fatalf("Append(%d) error = %v", i, err)
		}
	}

	if got := countExecQueries(exec.calls, "INSERT INTO vehicle_daily_mileage\n"); got != 2 {
		t.Fatalf("daily projections = %d, want 2", got)
	}
}

func TestWriterAppendUsesCurrentSampleWhenNoHistoricalBaseline(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	eventTime := time.Date(2026, 7, 8, 13, 20, 0, 0, loc)
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    eventTime.UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	mock.ExpectExec(`INSERT INTO vehicle_data_source`).
		WithArgs("JT808", "115.231.168.135", "115.231.168.135:20215", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", "JT808:13307765812@115.231.168.135").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}))
	mock.ExpectBegin()
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage_source`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"JT808:13307765812@115.231.168.135",
			"115.231.168.135",
			"115.231.168.135:20215",
			"13307765812",
			"",
			"",
			float64(4123.9),
			float64(4123.9),
			float64(0),
			int64(1),
			eventTime,
			eventTime,
			QualityOK,
			QualityReasonCurrentDayFirst,
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", int64(maxSelectedDailyMileageKM)).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`UPDATE vehicle_daily_mileage_source s`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			int64(maxSelectedDailyMileageKM),
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`DELETE FROM vehicle_daily_mileage`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectCommit()

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("Append() error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterCachesMissingBaselineAfterSuccessfulFirstSample(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	firstTime := time.Date(2026, 7, 8, 13, 20, 0, 0, loc)
	secondTime := firstTime.Add(5 * time.Second)
	base := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
	})

	mock.ExpectExec(`INSERT INTO vehicle_data_source`).
		WithArgs("JT808", "115.231.168.135", "115.231.168.135:20215", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", "JT808:13307765812@115.231.168.135").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}))
	mock.ExpectBegin()
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage_source`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"JT808:13307765812@115.231.168.135",
			"115.231.168.135",
			"115.231.168.135:20215",
			"13307765812",
			"",
			"",
			float64(4123.9),
			float64(4123.9),
			float64(0),
			int64(1),
			firstTime,
			firstTime,
			QualityOK,
			QualityReasonCurrentDayFirst,
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", int64(maxSelectedDailyMileageKM)).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`UPDATE vehicle_daily_mileage_source s`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			int64(maxSelectedDailyMileageKM),
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`DELETE FROM vehicle_daily_mileage`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectCommit()
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage_source`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"JT808:13307765812@115.231.168.135",
			"115.231.168.135",
			"115.231.168.135:20215",
			"13307765812",
			"",
			"",
			float64(4123.9),
			float64(4124.4),
			approxFloat64{want: 0.5, tolerance: 0.000001},
			int64(1),
			firstTime,
			secondTime,
			QualityOK,
			QualityReasonCurrentDayFirst,
		).
		WillReturnResult(sqlmock.NewResult(0, 1))

	first := base
	first.EventTimeMS = firstTime.UnixMilli()
	first.Fields = map[string]any{"jt808.location.total_mileage_km": 4123.9}
	if err := writer.Append(context.Background(), first); err != nil {
		t.Fatalf("first Append() error = %v", err)
	}
	second := base
	second.EventTimeMS = secondTime.UnixMilli()
	second.Fields = map[string]any{"jt808.location.total_mileage_km": 4124.4}
	if err := writer.Append(context.Background(), second); err != nil {
		t.Fatalf("second Append() error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterRetriesExpiredMissingPreviousDayBaseline(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	writer.SetBaselineMissTTL(0)
	candidate := SourceMileageSample{
		VIN:       "LA9GG64L7PBAF4001",
		StatDate:  "2026-07-13",
		Protocol:  envelope.ProtocolJT808,
		SourceKey: "JT808:64013036430@PLATFORM:g7s",
	}
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs(candidate.VIN, candidate.StatDate, "JT808", candidate.SourceKey).
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}))

	if _, found, err := writer.previousBaseline(context.Background(), candidate); err != nil || found {
		t.Fatalf("first previousBaseline() found=%v error=%v, want cacheable miss", found, err)
	}

	previousTime := time.Date(2026, 7, 12, 23, 58, 0, 0, loc)
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs(candidate.VIN, candidate.StatDate, "JT808", candidate.SourceKey).
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).AddRow(42567.9, previousTime))

	baseline, found, err := writer.previousBaseline(context.Background(), candidate)
	if err != nil || !found {
		t.Fatalf("second previousBaseline() found=%v error=%v, want recovered baseline", found, err)
	}
	if baseline.LatestTotalKM != 42567.9 || !baseline.LatestEventTime.Equal(previousTime) {
		t.Fatalf("baseline = %+v, want previous natural-day last sample", baseline)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterAppendUsesPreviousSourceBaselineForRealtimeCandidate(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	previousTime := time.Date(2026, 7, 7, 23, 58, 0, 0, loc)
	currentTime := time.Date(2026, 7, 8, 13, 20, 0, 0, loc)
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    currentTime.UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	mock.ExpectExec(`INSERT INTO vehicle_data_source`).
		WithArgs("JT808", "115.231.168.135", "115.231.168.135:20215", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", "JT808:13307765812@115.231.168.135").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(4100.8, previousTime))
	mock.ExpectBegin()
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage_source`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"JT808:13307765812@115.231.168.135",
			"115.231.168.135",
			"115.231.168.135:20215",
			"13307765812",
			"",
			"",
			float64(4100.8),
			float64(4123.9),
			approxFloat64{want: 23.1, tolerance: 0.000001},
			int64(1),
			previousTime,
			currentTime,
			QualityOK,
			QualityReasonHistorical,
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", int64(maxSelectedDailyMileageKM)).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`UPDATE vehicle_daily_mileage_source s`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			int64(maxSelectedDailyMileageKM),
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`DELETE FROM vehicle_daily_mileage`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectCommit()

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("Append() error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterAppendUsesOlderHistoricalSourceBaseline(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	historicalTime := time.Date(2026, 7, 4, 23, 58, 0, 0, loc)
	currentTime := time.Date(2026, 7, 8, 13, 20, 0, 0, loc)
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolYutongMQTT,
		VIN:            "LMRKH9AC2R1004087",
		DeviceID:       "LMRKH9AC2R1004087",
		SourceEndpoint: "mqtt://yutong/ytforward/shln/3",
		EventTimeMS:    currentTime.UnixMilli(),
		Fields: map[string]any{
			"yutong_mqtt.data.total_mileage": 120788000,
		},
	})

	mock.ExpectExec(`INSERT INTO vehicle_data_source`).
		WithArgs("YUTONG_MQTT", "mqtt", "mqtt://yutong/ytforward/shln/3", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LMRKH9AC2R1004087", "2026-07-08", "YUTONG_MQTT", "YUTONG_MQTT:LMRKH9AC2R1004087@mqtt").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(120672.0, historicalTime))
	mock.ExpectBegin()
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage_source`).
		WithArgs(
			"LMRKH9AC2R1004087",
			"2026-07-08",
			"YUTONG_MQTT",
			"YUTONG_MQTT:LMRKH9AC2R1004087@mqtt",
			"mqtt",
			"mqtt://yutong/ytforward/shln/3",
			"",
			"LMRKH9AC2R1004087",
			"",
			float64(120672.0),
			float64(120788.0),
			approxFloat64{want: 116.0, tolerance: 0.000001},
			int64(1),
			historicalTime,
			currentTime,
			QualityOK,
			QualityReasonHistorical,
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage`).
		WithArgs("LMRKH9AC2R1004087", "2026-07-08", "YUTONG_MQTT", int64(maxSelectedDailyMileageKM)).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`UPDATE vehicle_daily_mileage_source s`).
		WithArgs(
			"LMRKH9AC2R1004087",
			"2026-07-08",
			"YUTONG_MQTT",
			int64(maxSelectedDailyMileageKM),
			"LMRKH9AC2R1004087",
			"2026-07-08",
			"YUTONG_MQTT",
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`DELETE FROM vehicle_daily_mileage`).
		WithArgs(
			"LMRKH9AC2R1004087",
			"2026-07-08",
			"YUTONG_MQTT",
			"LMRKH9AC2R1004087",
			"2026-07-08",
			"YUTONG_MQTT",
		).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectCommit()

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("Append() error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterApplyRealtimeBaselineClampsSmallNegativeMileageJitter(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	baselineTime := time.Date(2026, 7, 11, 19, 32, 20, 0, loc)
	currentTime := time.Date(2026, 7, 12, 2, 38, 23, 0, loc)
	candidate := SourceMileageSample{
		VIN:             "LB9A32A28R0LS1574",
		StatDate:        "2026-07-12",
		Protocol:        envelope.ProtocolJT808,
		SourceKey:       "JT808:64115156034@115.159.85.149",
		SourceIP:        "115.159.85.149",
		LatestTotalKM:   15355.3,
		LatestEventTime: currentTime,
	}

	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LB9A32A28R0LS1574", "2026-07-12", "JT808", "JT808:64115156034@115.159.85.149").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(15355.4, baselineTime))

	if err := writer.applyRealtimeBaseline(context.Background(), &candidate); err != nil {
		t.Fatalf("applyRealtimeBaseline() error = %v", err)
	}
	if candidate.QualityStatus != QualityOK || candidate.QualityReason != "negative_jitter_clamped" {
		t.Fatalf("quality = %s/%s", candidate.QualityStatus, candidate.QualityReason)
	}
	if candidate.DailyKM != 0 {
		t.Fatalf("daily km = %v, want 0", candidate.DailyKM)
	}
	if candidate.FirstTotalKM != 15355.4 || !candidate.FirstEventTime.Equal(baselineTime) {
		t.Fatalf("baseline not preserved: %#v", candidate)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterApplyRealtimeBaselineKeepsNegativeHistoricalDeltaInvalid(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	baselineTime := time.Date(2026, 7, 11, 19, 32, 20, 0, loc)
	currentTime := time.Date(2026, 7, 12, 2, 38, 23, 0, loc)
	candidate := SourceMileageSample{
		VIN:             "LB9A32A28R0LS1574",
		StatDate:        "2026-07-12",
		Protocol:        envelope.ProtocolJT808,
		SourceKey:       "JT808:64115156034@115.159.85.149",
		SourceIP:        "115.159.85.149",
		LatestTotalKM:   15300.0,
		LatestEventTime: currentTime,
	}

	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LB9A32A28R0LS1574", "2026-07-12", "JT808", "JT808:64115156034@115.159.85.149").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(15355.4, baselineTime))

	if err := writer.applyRealtimeBaseline(context.Background(), &candidate); err != nil {
		t.Fatalf("applyRealtimeBaseline() error = %v", err)
	}
	if candidate.QualityStatus != QualityInvalidDelta || candidate.QualityReason != "outside_daily_range" {
		t.Fatalf("quality = %s/%s", candidate.QualityStatus, candidate.QualityReason)
	}
	if candidate.DailyKM > -55 || candidate.DailyKM < -56 {
		t.Fatalf("daily km = %v, want large negative delta", candidate.DailyKM)
	}
	if candidate.FirstTotalKM != 15355.4 || !candidate.FirstEventTime.Equal(baselineTime) {
		t.Fatalf("previous-day baseline not preserved: %#v", candidate)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterApplyRealtimeBaselineKeepsInvalidWhenCurrentDayBaselineIsStillHigher(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	previousTime := time.Date(2026, 7, 11, 19, 32, 20, 0, loc)
	currentTime := time.Date(2026, 7, 12, 2, 38, 23, 0, loc)
	candidate := SourceMileageSample{
		VIN:             "LB9A32A28R0LS1574",
		StatDate:        "2026-07-12",
		Protocol:        envelope.ProtocolJT808,
		SourceKey:       "JT808:64115156034@115.159.85.149",
		SourceIP:        "115.159.85.149",
		LatestTotalKM:   15300.0,
		LatestEventTime: currentTime,
	}

	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LB9A32A28R0LS1574", "2026-07-12", "JT808", "JT808:64115156034@115.159.85.149").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(15355.4, previousTime))

	if err := writer.applyRealtimeBaseline(context.Background(), &candidate); err != nil {
		t.Fatalf("applyRealtimeBaseline() error = %v", err)
	}
	if candidate.QualityStatus != QualityInvalidDelta || candidate.QualityReason != "outside_daily_range" {
		t.Fatalf("quality = %s/%s", candidate.QualityStatus, candidate.QualityReason)
	}
	if candidate.DailyKM > -55 || candidate.DailyKM < -56 {
		t.Fatalf("daily km = %v, want large negative delta", candidate.DailyKM)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterApplyRealtimeBaselineRejectsNegativeDeltaEvenWithLowerCurrentDaySample(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	previousTime := time.Date(2026, 7, 11, 19, 32, 20, 0, loc)
	currentTime := time.Date(2026, 7, 12, 2, 38, 23, 0, loc)
	candidate := SourceMileageSample{
		VIN:             "LB9A32A28R0LS1574",
		StatDate:        "2026-07-12",
		Protocol:        envelope.ProtocolJT808,
		SourceKey:       "JT808:64115156034@115.159.85.149",
		SourceIP:        "115.159.85.149",
		LatestTotalKM:   15300.0,
		LatestEventTime: currentTime,
	}

	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LB9A32A28R0LS1574", "2026-07-12", "JT808", "JT808:64115156034@115.159.85.149").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(15355.4, previousTime))

	if err := writer.applyRealtimeBaseline(context.Background(), &candidate); err != nil {
		t.Fatalf("applyRealtimeBaseline() error = %v", err)
	}
	if candidate.QualityStatus != QualityInvalidDelta || candidate.QualityReason != "outside_daily_range" {
		t.Fatalf("quality = %s/%s", candidate.QualityStatus, candidate.QualityReason)
	}
	if candidate.DailyKM > -55 || candidate.DailyKM < -56 {
		t.Fatalf("daily km = %v, want large negative delta", candidate.DailyKM)
	}
	if candidate.FirstTotalKM != 15355.4 || !candidate.FirstEventTime.Equal(previousTime) {
		t.Fatalf("previous-day baseline not preserved: %#v", candidate)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterApplyRealtimeBaselineAcceptsPlausibleMultiDayFallbackDelta(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	baselineTime := time.Date(2026, 7, 4, 11, 7, 58, 0, loc)
	currentTime := time.Date(2026, 7, 12, 2, 52, 47, 0, loc)
	candidate := SourceMileageSample{
		VIN:             "LNXNEGRR1SR319498",
		StatDate:        "2026-07-12",
		Protocol:        envelope.ProtocolGB32960,
		SourceKey:       "GB32960:unknown@8.134.95.166",
		SourceIP:        "8.134.95.166",
		LatestTotalKM:   16665.6,
		LatestEventTime: currentTime,
	}

	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LNXNEGRR1SR319498", "2026-07-12", "GB32960", "GB32960:unknown@8.134.95.166").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(8832.1, baselineTime))

	if err := writer.applyRealtimeBaseline(context.Background(), &candidate); err != nil {
		t.Fatalf("applyRealtimeBaseline() error = %v", err)
	}
	if candidate.QualityStatus != QualityOK || candidate.QualityReason != QualityReasonHistorical {
		t.Fatalf("quality = %s/%s", candidate.QualityStatus, candidate.QualityReason)
	}
	if candidate.DailyKM < 7833.4 || candidate.DailyKM > 7833.6 {
		t.Fatalf("daily km = %v, want multi-day gap delta", candidate.DailyKM)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterApplyRealtimeBaselineRejectsHistoricalBaselineJump(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	baselineTime := time.Date(2026, 7, 3, 19, 0, 39, 0, loc)
	currentTime := time.Date(2026, 7, 12, 10, 44, 56, 0, loc)
	candidate := SourceMileageSample{
		VIN:             "LNXNEGRR6SR319464",
		StatDate:        "2026-07-12",
		Protocol:        envelope.ProtocolGB32960,
		SourceKey:       "GB32960:unknown@8.134.95.166",
		SourceIP:        "8.134.95.166",
		LatestTotalKM:   40009.7,
		LatestEventTime: currentTime,
	}

	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LNXNEGRR6SR319464", "2026-07-12", "GB32960", "GB32960:unknown@8.134.95.166").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}).
			AddRow(10009.7, baselineTime))

	if err := writer.applyRealtimeBaseline(context.Background(), &candidate); err != nil {
		t.Fatalf("applyRealtimeBaseline() error = %v", err)
	}
	if candidate.QualityStatus != QualityInvalidDelta || candidate.QualityReason != "outside_daily_range" {
		t.Fatalf("quality = %s/%s", candidate.QualityStatus, candidate.QualityReason)
	}
	if candidate.DailyKM < 29999.9 || candidate.DailyKM > 30000.1 {
		t.Fatalf("daily km = %v, want historical jump delta", candidate.DailyKM)
	}
	if candidate.FirstTotalKM != 10009.7 || !candidate.FirstEventTime.Equal(baselineTime) {
		t.Fatalf("previous-day baseline not preserved: %#v", candidate)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterAppendMarksCandidateNoPreviousBaselineWhenPreviousBaselineMissing(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	currentTime := time.Date(2026, 7, 8, 15, 56, 0, 0, loc)
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    currentTime.UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4136.8,
		},
	})

	mock.ExpectExec(`INSERT INTO vehicle_data_source`).
		WithArgs("JT808", "115.231.168.135", "115.231.168.135:20215", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", "JT808:13307765812@115.231.168.135").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}))
	mock.ExpectBegin()
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage_source`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"JT808:13307765812@115.231.168.135",
			"115.231.168.135",
			"115.231.168.135:20215",
			"13307765812",
			"",
			"",
			float64(4136.8),
			float64(4136.8),
			float64(0),
			int64(1),
			currentTime,
			currentTime,
			QualityOK,
			QualityReasonCurrentDayFirst,
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", int64(maxSelectedDailyMileageKM)).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`UPDATE vehicle_daily_mileage_source s`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			int64(maxSelectedDailyMileageKM),
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`DELETE FROM vehicle_daily_mileage`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
		).
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectCommit()

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("Append() error = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestWriterEnsuresSchemaAndUpsertsDailyMileage(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	if err := writer.EnsureSchema(context.Background()); err != nil {
		t.Fatalf("EnsureSchema() error = %v", err)
	}
	if err := writer.Append(context.Background(), managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolGB32960,
		VIN:            "LNBVIN00000000002",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    time.Date(2026, 7, 1, 9, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"gb32960.vehicle.total_mileage_km": "10000.0",
		},
	})); err != nil {
		t.Fatalf("Append() error = %v", err)
	}

	schemaCalls := 3 + len(DailyMileageAlterSQL)
	if len(exec.calls) != schemaCalls+5 {
		t.Fatalf("exec calls = %d", len(exec.calls))
	}
	for i, want := range []string{
		"CREATE TABLE IF NOT EXISTS vehicle_data_source",
		"CREATE TABLE IF NOT EXISTS vehicle_daily_mileage_source",
		"CREATE TABLE IF NOT EXISTS vehicle_daily_mileage",
	} {
		if !strings.Contains(exec.calls[i].query, want) {
			t.Fatalf("schema call %d = %s, want %s", i, exec.calls[i].query, want)
		}
	}
	for _, column := range []string{"vehicle_key", "AUTO_INCREMENT", "created_at", "first_total_mileage_km", "trusted_source_key", "trusted_phone", "trusted_source_endpoint", "sample_count"} {
		if strings.Contains(exec.calls[2].query, column) {
			t.Fatalf("schema should not include %s: %s", column, exec.calls[2].query)
		}
	}
	for _, column := range []string{"source_id BIGINT NULL", "daily_mileage_km", "latest_total_mileage_km", "KEY idx_source_id (source_id)"} {
		if !strings.Contains(exec.calls[2].query, column) {
			t.Fatalf("schema should include %s: %s", column, exec.calls[2].query)
		}
	}
	if !strings.Contains(exec.calls[2].query, "PRIMARY KEY (vin, stat_date, protocol)") {
		t.Fatalf("daily mileage table should key by vin/stat_date/protocol: %s", exec.calls[2].query)
	}
	if strings.Contains(exec.calls[2].query, "KEY idx_vin (vin)") {
		t.Fatalf("daily mileage table should not keep redundant vin index covered by the primary key: %s", exec.calls[2].query)
	}
	projectCall := exec.calls[schemaCalls+2]
	if !strings.Contains(projectCall.query, "INSERT INTO vehicle_daily_mileage") {
		t.Fatalf("unexpected project sql: %s", projectCall.query)
	}
	if !strings.Contains(projectCall.query, "ON DUPLICATE KEY UPDATE") {
		t.Fatalf("unexpected upsert sql: %s", projectCall.query)
	}
	if strings.Contains(projectCall.query, "metric_key") || strings.Contains(projectCall.query, "metric_unit") {
		t.Fatalf("daily mileage upsert should not use generic metric columns: %s", projectCall.query)
	}
	if !strings.Contains(projectCall.query, "JOIN vehicle_data_source ds") {
		t.Fatalf("project sql should join data source: %s", projectCall.query)
	}
	if !strings.Contains(projectCall.query, "ds.id") {
		t.Fatalf("project sql should write source id: %s", projectCall.query)
	}
	for _, forbidden := range []string{"trusted_source_key", "trusted_phone", "trusted_source_endpoint", "first_total_mileage_km", "sample_count)"} {
		if strings.Contains(projectCall.query, forbidden) {
			t.Fatalf("project sql should not write final-table field %q: %s", forbidden, projectCall.query)
		}
	}
	if got := projectCall.args[0]; got != "LNBVIN00000000002" {
		t.Fatalf("first upsert arg should be vin, got %#v", got)
	}

	if !strings.Contains(exec.calls[schemaCalls].query, "INSERT INTO vehicle_data_source") {
		t.Fatalf("source upsert query missing: %s", exec.calls[schemaCalls].query)
	}
	if !strings.Contains(exec.calls[schemaCalls+1].query, "INSERT INTO vehicle_daily_mileage_source") {
		t.Fatalf("candidate upsert query missing: %s", exec.calls[schemaCalls+1].query)
	}
}

func TestWriterAppendWithResultReportsProjectionThrottle(t *testing.T) {
	exec := &recordingExec{}
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(exec, loc)
	base := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})

	first := base
	first.EventTimeMS = time.Date(2026, 7, 8, 13, 20, 0, 0, loc).UnixMilli()
	firstResult, err := writer.AppendWithResult(context.Background(), first)
	if err != nil {
		t.Fatalf("first AppendWithResult() error = %v", err)
	}
	if firstResult.ProjectionsAttempted != 1 || firstResult.ProjectionsWritten != 1 || firstResult.ProjectionsSkippedThrottled != 0 {
		t.Fatalf("first projection result = %+v", firstResult)
	}

	second := base
	second.EventTimeMS = time.Date(2026, 7, 8, 13, 20, 5, 0, loc).UnixMilli()
	second.Fields = map[string]any{"jt808.location.total_mileage_km": 4124.2}
	secondResult, err := writer.AppendWithResult(context.Background(), second)
	if err != nil {
		t.Fatalf("second AppendWithResult() error = %v", err)
	}
	if secondResult.SamplesWritten != 1 || secondResult.ProjectionsAttempted != 0 || secondResult.ProjectionsWritten != 0 || secondResult.ProjectionsSkippedThrottled != 1 {
		t.Fatalf("second projection result = %+v, want source write with throttled final projection", secondResult)
	}
	if secondResult.SourceTouchesSkippedThrottled != 1 || secondResult.SourceTouchesWritten != 0 {
		t.Fatalf("second source result = %+v, want source touch throttled", secondResult)
	}

	third := base
	third.EventTimeMS = time.Date(2026, 7, 8, 13, 20, 16, 0, loc).UnixMilli()
	third.Fields = map[string]any{"jt808.location.total_mileage_km": 4125.0}
	thirdResult, err := writer.AppendWithResult(context.Background(), third)
	if err != nil {
		t.Fatalf("third AppendWithResult() error = %v", err)
	}
	if thirdResult.ProjectionsAttempted != 1 || thirdResult.ProjectionsWritten != 1 || thirdResult.ProjectionsSkippedThrottled != 0 {
		t.Fatalf("third projection result = %+v", thirdResult)
	}
}

func TestWriterSkipsConsecutiveDuplicateMileageSamples(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LNBVIN00000000001",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    time.Date(2026, 7, 1, 9, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 10241.2,
		},
	})

	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("first Append() error = %v", err)
	}
	event.ReceivedAtMS += 1000
	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("duplicate Append() error = %v", err)
	}

	if len(exec.calls) != 5 {
		t.Fatalf("exec calls = %d, want 5", len(exec.calls))
	}
}

func TestWriterAppendWithResultReportsDuplicateMileage(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LNBVIN00000000001",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    time.Date(2026, 7, 1, 9, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 10241.2,
		},
	})

	if _, err := writer.AppendWithResult(context.Background(), event); err != nil {
		t.Fatalf("first AppendWithResult() error = %v", err)
	}
	event.ReceivedAtMS += 1000
	result, err := writer.AppendWithResult(context.Background(), event)
	if err != nil {
		t.Fatalf("duplicate AppendWithResult() error = %v", err)
	}

	if result.SamplesFound != 1 || result.SamplesSkippedSameMileage != 1 || result.SamplesWritten != 0 {
		t.Fatalf("result = %+v, want duplicate mileage skipped", result)
	}
	if result.SourceTouchesSkippedThrottled != 1 || result.SourceTouchesWritten != 0 {
		t.Fatalf("source touch result = %+v, want duplicate to throttle source touch", result)
	}
	if len(exec.calls) != 5 {
		t.Fatalf("exec calls = %d, want duplicate to avoid mysql writes", len(exec.calls))
	}
}

func TestWriterDoesNotCacheMileageWhenUpsertFails(t *testing.T) {
	exec := &recordingExec{errs: []error{errors.New("mysql unavailable"), nil}}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LNBVIN00000000001",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    time.Date(2026, 7, 1, 9, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 10241.2,
		},
	})

	if err := writer.Append(context.Background(), event); err == nil {
		t.Fatalf("first Append() error = nil, want mysql error")
	}
	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("retry Append() error = %v", err)
	}

	if len(exec.calls) != 6 {
		t.Fatalf("exec calls = %d, want 6", len(exec.calls))
	}
}

func TestWriterRollsBackMileageTransactionWhenProjectionFails(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New() error = %v", err)
	}
	defer db.Close()

	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(db, loc)
	eventTime := time.Date(2026, 7, 8, 9, 30, 0, 0, loc)
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LA9GG64L7PBAF4001",
		Phone:          "13307765812",
		SourceEndpoint: "115.231.168.135:20215",
		EventTimeMS:    eventTime.UnixMilli(),
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 4123.9,
		},
	})
	wantErr := errors.New("project final mileage failed")

	mock.ExpectExec(`INSERT INTO vehicle_data_source`).
		WithArgs("JT808", "115.231.168.135", "115.231.168.135:20215", sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg(), sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectQuery(`SELECT latest_total_mileage_km, latest_event_time FROM vehicle_daily_mileage_source`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", "JT808:13307765812@115.231.168.135").
		WillReturnRows(sqlmock.NewRows([]string{"latest_total_mileage_km", "latest_event_time"}))
	mock.ExpectBegin()
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage_source`).
		WithArgs(
			"LA9GG64L7PBAF4001",
			"2026-07-08",
			"JT808",
			"JT808:13307765812@115.231.168.135",
			"115.231.168.135",
			"115.231.168.135:20215",
			"13307765812",
			"",
			"",
			float64(4123.9),
			float64(4123.9),
			float64(0),
			int64(1),
			eventTime,
			eventTime,
			QualityOK,
			QualityReasonCurrentDayFirst,
		).
		WillReturnResult(sqlmock.NewResult(0, 1))
	mock.ExpectExec(`INSERT INTO vehicle_daily_mileage`).
		WithArgs("LA9GG64L7PBAF4001", "2026-07-08", "JT808", int64(maxSelectedDailyMileageKM)).
		WillReturnError(wantErr)
	mock.ExpectRollback()

	err = writer.Append(context.Background(), event)
	if !errors.Is(err, wantErr) {
		t.Fatalf("Append() error = %v, want %v", err, wantErr)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
	writer.mu.Lock()
	defer writer.mu.Unlock()
	if len(writer.lastTotalMileage) != 0 || len(writer.lastProjection) != 0 {
		t.Fatalf("writer cache should not mark failed sample, mileage=%d projection=%d", len(writer.lastTotalMileage), len(writer.lastProjection))
	}
}

func TestWriterKeepsOnlyLatestDateInMileageCache(t *testing.T) {
	exec := &recordingExec{}
	writer := NewWriter(exec, time.FixedZone("Asia/Shanghai", 8*3600))
	event := managedTestSource(envelope.FrameEnvelope{
		Protocol:       envelope.ProtocolJT808,
		VIN:            "LNBVIN00000000001",
		SourceEndpoint: "115.231.168.135:20215",
		Fields: map[string]any{
			"jt808.location.total_mileage_km": 10241.2,
		},
	})

	event.EventTimeMS = time.Date(2026, 7, 1, 23, 59, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli()
	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("first Append() error = %v", err)
	}
	event.EventTimeMS = time.Date(2026, 7, 2, 0, 1, 0, 0, time.FixedZone("Asia/Shanghai", 8*3600)).UnixMilli()
	if err := writer.Append(context.Background(), event); err != nil {
		t.Fatalf("next-day Append() error = %v", err)
	}

	if len(exec.calls) != 10 {
		t.Fatalf("exec calls = %d, want 10", len(exec.calls))
	}
	if len(writer.lastTotalMileage) != 1 {
		t.Fatalf("cache entries = %d, want 1", len(writer.lastTotalMileage))
	}
}

func TestWriterCacheLimitEvictsOldestEntries(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(&recordingExec{}, loc)
	writer.SetMaxCacheEntries(2)
	samples := []struct {
		sample   MetricSample
		sourceIP string
	}{
		{sample: MetricSample{VIN: "VIN00000000000001", Protocol: envelope.ProtocolJT808, StatDate: "2026-07-01", SourceKey: "JT808:a@115.231.168.135", EventTime: time.Date(2026, 7, 1, 8, 0, 0, 0, loc)}, sourceIP: "115.231.168.135"},
		{sample: MetricSample{VIN: "VIN00000000000002", Protocol: envelope.ProtocolJT808, StatDate: "2026-07-02", SourceKey: "JT808:b@115.231.168.136", EventTime: time.Date(2026, 7, 2, 8, 0, 0, 0, loc)}, sourceIP: "115.231.168.136"},
		{sample: MetricSample{VIN: "VIN00000000000003", Protocol: envelope.ProtocolJT808, StatDate: "2026-07-03", SourceKey: "JT808:c@115.231.168.137", EventTime: time.Date(2026, 7, 3, 8, 0, 0, 0, loc)}, sourceIP: "115.231.168.137"},
	}
	for index, item := range samples {
		sample := item.sample
		sample.TotalMileageKM = float64(100 + index)
		writer.markMileageWritten(sample)
		writer.markProjected(sample)
		writer.markSourceTouched(SourceIdentity{
			Protocol:       sample.Protocol,
			SourceIP:       item.sourceIP,
			SourceEndpoint: item.sourceIP + ":20215",
		}, sample.EventTime)
		writer.mu.Lock()
		writer.baselineCache[mileageCacheKey(sample)] = sourceBaselineCacheEntry{found: true, baseline: sourceBaseline{LatestTotalKM: sample.TotalMileageKM, LatestEventTime: sample.EventTime}}
		writer.addCacheKeyLocked(writer.baselineKeysByPrefix, mileageCachePrefix(sample), mileageCacheKey(sample))
		writer.enforceCacheLimitsLocked()
		writer.mu.Unlock()
	}

	stats := writer.CacheStats()
	if stats.MaxEntries != 2 {
		t.Fatalf("max entries = %d, want 2", stats.MaxEntries)
	}
	if stats.LastTotalMileageEntries != 2 ||
		stats.LastProjectionEntries != 2 ||
		stats.LastSourceSeenEntries != 2 ||
		stats.BaselineEntries != 2 {
		t.Fatalf("cache entries = %+v, want each cache capped at 2", stats)
	}
	if stats.TotalMileageEvictions == 0 ||
		stats.ProjectionEvictions == 0 ||
		stats.SourceSeenEvictions == 0 ||
		stats.BaselineEvictions == 0 {
		t.Fatalf("cache evictions = %+v, want all caches to evict", stats)
	}
	if _, ok := writer.lastTotalMileage[mileageCacheKey(samples[0].sample)]; ok {
		t.Fatalf("oldest mileage cache key was not evicted")
	}
	if _, ok := writer.lastProjection[projectionCacheKey(samples[0].sample)]; ok {
		t.Fatalf("oldest projection cache key was not evicted")
	}
	if _, ok := writer.baselineCache[mileageCacheKey(samples[0].sample)]; ok {
		t.Fatalf("oldest baseline cache key was not evicted")
	}
	if _, ok := writer.lastSourceSeen["JT808|115.231.168.135"]; ok {
		t.Fatalf("oldest source touch cache key was not evicted")
	}
}

func TestWriterCleanupCachesDropsExpiredVehicleState(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(&recordingExec{}, loc)
	writer.SetCacheRetention(48 * time.Hour)
	writer.SetCacheCleanupInterval(0)
	oldSample := MetricSample{
		VIN:       "OLDVIN00000000001",
		Protocol:  envelope.ProtocolJT808,
		StatDate:  "2026-07-01",
		SourceKey: "JT808:old@115.231.168.135",
		EventTime: time.Date(2026, 7, 1, 12, 0, 0, 0, loc),
	}
	currentSample := MetricSample{
		VIN:       "NEWVIN00000000001",
		Protocol:  envelope.ProtocolJT808,
		StatDate:  "2026-07-04",
		SourceKey: "JT808:new@115.231.168.136",
		EventTime: time.Date(2026, 7, 4, 12, 0, 0, 0, loc),
	}
	oldMileageKey := mileageCacheKey(oldSample)
	currentMileageKey := mileageCacheKey(currentSample)
	oldProjectionKey := projectionCacheKey(oldSample)
	currentProjectionKey := projectionCacheKey(currentSample)
	writer.lastTotalMileage[oldMileageKey] = 100
	writer.lastTotalMileage[currentMileageKey] = 200
	writer.baselineCache[oldMileageKey] = sourceBaselineCacheEntry{found: true}
	writer.baselineCache[currentMileageKey] = sourceBaselineCacheEntry{found: true}
	writer.lastProjection[oldProjectionKey] = projectionCacheEntry{projectedAt: oldSample.EventTime, sourceKeys: map[string]struct{}{oldSample.SourceKey: {}}}
	writer.lastProjection[currentProjectionKey] = projectionCacheEntry{projectedAt: currentSample.EventTime, sourceKeys: map[string]struct{}{currentSample.SourceKey: {}}}
	writer.lastSourceSeen["JT808|115.231.168.135"] = oldSample.EventTime
	writer.lastSourceSeen["JT808|115.231.168.136"] = currentSample.EventTime

	writer.maybeCleanupCaches(time.Date(2026, 7, 4, 12, 1, 0, 0, loc))

	if _, ok := writer.lastTotalMileage[oldMileageKey]; ok {
		t.Fatalf("expired mileage cache key was not removed")
	}
	if _, ok := writer.baselineCache[oldMileageKey]; ok {
		t.Fatalf("expired baseline cache key was not removed")
	}
	if _, ok := writer.lastProjection[oldProjectionKey]; ok {
		t.Fatalf("expired projection cache key was not removed")
	}
	if _, ok := writer.lastSourceSeen["JT808|115.231.168.135"]; ok {
		t.Fatalf("expired source touch cache key was not removed")
	}
	if _, ok := writer.lastTotalMileage[currentMileageKey]; !ok {
		t.Fatalf("current mileage cache key was removed")
	}
	if _, ok := writer.baselineCache[currentMileageKey]; !ok {
		t.Fatalf("current baseline cache key was removed")
	}
	if _, ok := writer.lastProjection[currentProjectionKey]; !ok {
		t.Fatalf("current projection cache key was removed")
	}
	if _, ok := writer.lastSourceSeen["JT808|115.231.168.136"]; !ok {
		t.Fatalf("current source touch cache key was removed")
	}
	stats := writer.CacheStats()
	if stats.LastTotalMileageEntries != 1 ||
		stats.BaselineEntries != 1 ||
		stats.LastProjectionEntries != 1 ||
		stats.LastSourceSeenEntries != 1 {
		t.Fatalf("cache stats entries = %+v, want one current entry in each cache", stats)
	}
	if stats.LastCleanupTotalMileage != 1 ||
		stats.LastCleanupBaseline != 1 ||
		stats.LastCleanupProjection != 1 ||
		stats.LastCleanupSourceSeen != 1 {
		t.Fatalf("cache cleanup stats = %+v, want one deleted from each cache", stats)
	}
	if !stats.LastCleanupAt.Equal(time.Date(2026, 7, 4, 12, 1, 0, 0, loc)) {
		t.Fatalf("last cleanup at = %s", stats.LastCleanupAt)
	}
}

func TestWriterCleanupCachesCanBeDisabled(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	writer := NewWriter(&recordingExec{}, loc)
	writer.SetCacheRetention(0)
	writer.SetCacheCleanupInterval(0)
	oldSample := MetricSample{
		VIN:       "OLDVIN00000000001",
		Protocol:  envelope.ProtocolJT808,
		StatDate:  "2026-07-01",
		SourceKey: "JT808:old@115.231.168.135",
		EventTime: time.Date(2026, 7, 1, 12, 0, 0, 0, loc),
	}
	key := mileageCacheKey(oldSample)
	writer.lastTotalMileage[key] = 100
	writer.baselineCache[key] = sourceBaselineCacheEntry{found: true}
	writer.lastProjection[projectionCacheKey(oldSample)] = projectionCacheEntry{projectedAt: oldSample.EventTime}
	writer.lastSourceSeen["JT808|115.231.168.135"] = oldSample.EventTime

	writer.maybeCleanupCaches(time.Date(2026, 7, 10, 12, 0, 0, 0, loc))

	if len(writer.lastTotalMileage) != 1 || len(writer.baselineCache) != 1 || len(writer.lastProjection) != 1 || len(writer.lastSourceSeen) != 1 {
		t.Fatalf("cache cleanup should be disabled, got mileage=%d baseline=%d projection=%d source=%d", len(writer.lastTotalMileage), len(writer.baselineCache), len(writer.lastProjection), len(writer.lastSourceSeen))
	}
}

func countExecQueries(calls []execCall, fragment string) int {
	count := 0
	for _, call := range calls {
		if strings.Contains(call.query, fragment) {
			count++
		}
	}
	return count
}

func TestWriterSupportsConcurrentPartitionConsumers(t *testing.T) {
	loc := time.FixedZone("Asia/Shanghai", 8*3600)
	exec := &concurrentExec{}
	writer := NewWriter(exec, loc)
	writer.SetSourceTouchInterval(0)
	writer.SetProjectionInterval(0)

	const workers = 32
	var wait sync.WaitGroup
	errorsCh := make(chan error, workers)
	for index := 0; index < workers; index++ {
		wait.Add(1)
		go func(index int) {
			defer wait.Done()
			env := managedTestSource(envelope.FrameEnvelope{
				Protocol:       envelope.ProtocolJT808,
				VIN:            fmt.Sprintf("LNBVIN%011d", index),
				Phone:          fmt.Sprintf("13%09d", index),
				MessageID:      "0x0200",
				SourceEndpoint: fmt.Sprintf("10.0.0.%d:808", index+1),
				EventTimeMS:    time.Date(2026, 7, 13, 8, 0, index, 0, loc).UnixMilli(),
				ReceivedAtMS:   time.Date(2026, 7, 13, 8, 0, index, 0, loc).UnixMilli(),
				Fields: map[string]any{
					"jt808.location.total_mileage_km": 1000 + float64(index),
				},
			})
			result, err := writer.AppendWithResult(context.Background(), env)
			if err != nil {
				errorsCh <- err
				return
			}
			if result.SamplesWritten != 1 {
				errorsCh <- fmt.Errorf("worker %d samples written = %d", index, result.SamplesWritten)
			}
		}(index)
	}
	wait.Wait()
	close(errorsCh)
	for err := range errorsCh {
		t.Fatal(err)
	}
	if exec.CallCount() == 0 {
		t.Fatal("concurrent writers did not execute any MySQL statements")
	}
}

type execCall struct {
	query string
	args  []any
}

type recordingExec struct {
	calls []execCall
	errs  []error
}

type concurrentExec struct {
	mu    sync.Mutex
	calls int
}

func (e *concurrentExec) ExecContext(_ context.Context, _ string, _ ...any) (sql.Result, error) {
	e.mu.Lock()
	e.calls++
	e.mu.Unlock()
	return nil, nil
}

func (e *concurrentExec) CallCount() int {
	e.mu.Lock()
	defer e.mu.Unlock()
	return e.calls
}

func (e *recordingExec) ExecContext(_ context.Context, query string, args ...any) (sql.Result, error) {
	e.calls = append(e.calls, execCall{query: query, args: args})
	if len(e.errs) > 0 {
		err := e.errs[0]
		e.errs = e.errs[1:]
		return nil, err
	}
	return nil, nil
}

type approxFloat64 struct {
	want      float64
	tolerance float64
}

func (m approxFloat64) Match(value driver.Value) bool {
	got, ok := value.(float64)
	if !ok {
		return false
	}
	return math.Abs(got-m.want) <= m.tolerance
}
