package platform

import (
	"context"
	"fmt"
	"net/url"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"
)

func exportAdminContext() context.Context {
	return WithPrincipal(context.Background(), Principal{SubjectID: "1", Name: "平台管理员", Username: "admin", Role: "admin", UserType: "admin", AuthProvider: "local"})
}

func TestHistoryExportIndexSurvivesServiceRestart(t *testing.T) {
	dir := t.TempDir()
	first := NewServiceWithRuntime(NewMockStore(), RuntimeInfo{ExportDir: dir})
	completedAt := time.Now().UTC().Format(time.RFC3339)
	filePath := filepath.Join(dir, "exp_completed.csv")
	if err := os.WriteFile(filePath, []byte("vin\nVIN001\n"), 0o640); err != nil {
		t.Fatal(err)
	}
	first.exportsMu.Lock()
	first.exports["exp_completed"] = &HistoryExportJob{ID: "exp_completed", Name: "完成任务", Status: "completed", Progress: 100, Format: "csv", Category: "location", CreatedAt: completedAt, UpdatedAt: completedAt, DownloadURL: "/api/v2/exports/exp_completed/download", filePath: filePath}
	first.exports["exp_running"] = &HistoryExportJob{ID: "exp_running", Name: "中断任务", Status: "running", Progress: 50, Format: "csv", Category: "location", CreatedAt: completedAt, UpdatedAt: completedAt}
	if err := first.persistHistoryExportsLocked(); err != nil {
		first.exportsMu.Unlock()
		t.Fatal(err)
	}
	first.exportsMu.Unlock()

	restarted := NewServiceWithRuntime(NewMockStore(), RuntimeInfo{ExportDir: dir})
	ctx := exportAdminContext()
	jobs := restarted.ListHistoryExports(ctx)
	if len(jobs) != 2 {
		t.Fatalf("jobs=%+v", jobs)
	}
	path, _, err := restarted.HistoryExportFile(ctx, "exp_completed")
	if err != nil || path != filePath {
		t.Fatalf("completed export not restored: path=%q err=%v", path, err)
	}
	for _, job := range jobs {
		if job.ID == "exp_running" && (job.Status != "failed" || !strings.Contains(job.Error, "服务重启")) {
			t.Fatalf("interrupted export should be failed: %+v", job)
		}
	}
}

type countingStore struct {
	*MockStore
	vehiclesCalls              int
	vehicleRealtimeCalls       int
	overviewBatchCalls         int
	lastVehicleQuery           url.Values
	lastRealtimeQuery          url.Values
	lastHistoryQuery           url.Values
	lastDailyMileageQuery      url.Values
	lastMileageStatisticsQuery url.Values
}

func newCountingStore() *countingStore {
	return &countingStore{MockStore: NewMockStore()}
}

func (s *countingStore) Vehicles(ctx context.Context, query url.Values) (Page[VehicleRow], error) {
	s.vehiclesCalls++
	s.lastVehicleQuery = cloneValues(query)
	return s.MockStore.Vehicles(ctx, query)
}

func (s *countingStore) VehicleRealtime(ctx context.Context, query url.Values) (Page[VehicleRealtimeRow], error) {
	s.vehicleRealtimeCalls++
	s.lastRealtimeQuery = cloneValues(query)
	return s.MockStore.VehicleRealtime(ctx, query)
}

func (s *countingStore) HistoryLocationsFromTDengine(ctx context.Context, query url.Values) (Page[HistoryLocationRow], error) {
	s.lastHistoryQuery = cloneValues(query)
	return s.MockStore.HistoryLocationsFromTDengine(ctx, query)
}

func (s *countingStore) DailyMileage(ctx context.Context, query url.Values) (Page[DailyMileageRow], error) {
	s.lastDailyMileageQuery = cloneValues(query)
	return s.MockStore.DailyMileage(ctx, query)
}

func (s *countingStore) MileageStatistics(ctx context.Context, query url.Values) (MileageStatistics, error) {
	s.lastMileageStatisticsQuery = cloneValues(query)
	return s.MockStore.MileageStatistics(ctx, query)
}

func TestCustomerVehicleScopeIsInjectedAndExplicitBypassIsDenied(t *testing.T) {
	store := newCountingStore()
	service := NewService(store)
	principal := Principal{Name: "客户甲", Role: "customer", UserType: "customer", VehicleVINs: []string{"LB9A32A24R0LS1426"}}
	ctx := WithPrincipal(context.Background(), principal)
	if _, err := service.Vehicles(ctx, url.Values{"limit": {"20"}}); err != nil {
		t.Fatalf("scoped vehicle list failed: %v", err)
	}
	if got := store.lastVehicleQuery.Get("scopeVins"); got != "LB9A32A24R0LS1426" {
		t.Fatalf("scopeVins=%q", got)
	}
	_, err := service.VehicleRealtime(ctx, url.Values{"vin": {"LMRKH9AC2R1004087"}})
	if clientErr, ok := asClientError(err); !ok || clientErr.Code != "VEHICLE_PERMISSION_DENIED" {
		t.Fatalf("cross-vehicle query should be forbidden, err=%v", err)
	}
}

func TestCustomerHistoryIsClampedToVehicleGrantStart(t *testing.T) {
	store := newCountingStore()
	service := NewService(store)
	validFrom := time.Date(2026, 7, 10, 12, 34, 56, 0, time.FixedZone("Asia/Shanghai", 8*60*60))
	principal := Principal{
		Name: "客户甲", Role: "customer", UserType: "customer",
		VehicleVINs:   []string{"LB9A32A24R0LS1426"},
		VehicleGrants: []VehicleGrant{{VIN: "LB9A32A24R0LS1426", ValidFrom: validFrom}},
	}
	ctx := WithPrincipal(context.Background(), principal)
	_, err := service.HistoryLocations(ctx, url.Values{
		"vin":      {"LB9A32A24R0LS1426"},
		"dateFrom": {"2026-07-01T00:00:00+08:00"},
		"dateTo":   {"2026-07-11T00:00:00+08:00"},
	})
	if err != nil {
		t.Fatalf("history query failed: %v", err)
	}
	if got := store.lastHistoryQuery.Get("dateFrom"); got != validFrom.Format(time.RFC3339) {
		t.Fatalf("dateFrom=%q want=%q", got, validFrom.Format(time.RFC3339))
	}
}

func TestCustomerHistoryBeforeGrantIsForbidden(t *testing.T) {
	service := NewService(newCountingStore())
	validFrom := time.Date(2026, 7, 10, 12, 34, 56, 0, time.FixedZone("Asia/Shanghai", 8*60*60))
	ctx := WithPrincipal(context.Background(), Principal{
		Name: "客户甲", Role: "customer", UserType: "customer",
		VehicleVINs:   []string{"LB9A32A24R0LS1426"},
		VehicleGrants: []VehicleGrant{{VIN: "LB9A32A24R0LS1426", ValidFrom: validFrom}},
	})
	_, err := service.HistoryLocations(ctx, url.Values{
		"vin":      {"LB9A32A24R0LS1426"},
		"dateFrom": {"2026-07-01T00:00:00+08:00"},
		"dateTo":   {"2026-07-10T12:34:56+08:00"},
	})
	clientErr, ok := asClientError(err)
	if !ok || clientErr.Code != "HISTORY_BEFORE_AUTHORIZATION" {
		t.Fatalf("expected HISTORY_BEFORE_AUTHORIZATION, err=%v", err)
	}
}

func TestCustomerMileageStartsAtFirstCompleteAuthorizedDay(t *testing.T) {
	store := newCountingStore()
	service := NewService(store)
	validFrom := time.Date(2026, 7, 10, 12, 34, 56, 0, time.FixedZone("Asia/Shanghai", 8*60*60))
	ctx := WithPrincipal(context.Background(), Principal{
		Name: "客户甲", Role: "customer", UserType: "customer",
		VehicleVINs:   []string{"LB9A32A24R0LS1426"},
		VehicleGrants: []VehicleGrant{{VIN: "LB9A32A24R0LS1426", ValidFrom: validFrom}},
	})
	if _, err := service.DailyMileage(ctx, url.Values{
		"vins":     {"LB9A32A24R0LS1426"},
		"dateFrom": {"2026-07-01"},
		"dateTo":   {"2026-07-15"},
	}); err != nil {
		t.Fatalf("daily mileage failed: %v", err)
	}
	if got := store.lastDailyMileageQuery.Get("scopeVehicleFrom"); got != `{"LB9A32A24R0LS1426":"2026-07-11"}` {
		t.Fatalf("scopeVehicleFrom=%q", got)
	}
}

func TestCustomerMileageFailsClosedWithoutGrantTime(t *testing.T) {
	service := NewService(newCountingStore())
	ctx := WithPrincipal(context.Background(), Principal{
		Name: "客户甲", Role: "customer", UserType: "customer",
		VehicleVINs: []string{"LB9A32A24R0LS1426"},
	})
	_, err := service.MileageStatistics(ctx, url.Values{
		"vins":     {"LB9A32A24R0LS1426"},
		"dateFrom": {"2026-07-01"},
		"dateTo":   {"2026-07-15"},
	})
	clientErr, ok := asClientError(err)
	if !ok || clientErr.Code != "HISTORY_SCOPE_UNAVAILABLE" {
		t.Fatalf("expected HISTORY_SCOPE_UNAVAILABLE, err=%v", err)
	}
}

func TestCustomerSourceEvidenceUsesOnlyCompleteAuthorizedDays(t *testing.T) {
	service := NewService(newCountingStore())
	shanghai := time.FixedZone("Asia/Shanghai", 8*60*60)
	validFrom := time.Date(2026, 7, 10, 12, 34, 56, 0, shanghai)
	validTo := time.Date(2026, 7, 15, 10, 0, 0, 0, shanghai)
	ctx := WithPrincipal(context.Background(), Principal{
		Name: "客户甲", Role: "customer", UserType: "customer",
		VehicleVINs:   []string{"LB9A32A24R0LS1426"},
		VehicleGrants: []VehicleGrant{{VIN: "LB9A32A24R0LS1426", ValidFrom: validFrom, ValidTo: &validTo}},
	})
	if _, err := service.VehicleSourceEvidence(ctx, "LB9A32A24R0LS1426", "2026-07-10"); err == nil {
		t.Fatal("partial authorization start day should not expose daily source evidence")
	} else if clientErr, ok := asClientError(err); !ok || clientErr.Code != "HISTORY_BEFORE_AUTHORIZATION" {
		t.Fatalf("expected HISTORY_BEFORE_AUTHORIZATION, err=%v", err)
	}
	if _, err := service.VehicleSourceEvidence(ctx, "LB9A32A24R0LS1426", "2026-07-11"); err != nil {
		t.Fatalf("first complete authorization day should be allowed: %v", err)
	}
	if _, err := service.VehicleSourceEvidence(ctx, "LB9A32A24R0LS1426", "2026-07-15"); err == nil {
		t.Fatal("partial authorization end day should not expose daily source evidence")
	} else if clientErr, ok := asClientError(err); !ok || clientErr.Code != "HISTORY_AFTER_AUTHORIZATION" {
		t.Fatalf("expected HISTORY_AFTER_AUTHORIZATION, err=%v", err)
	}
}

func TestVehicleSourceDiagnosticExposesOpaqueReferencesAndElectionReasons(t *testing.T) {
	service := NewService(NewMockStore())
	ctx := WithPrincipal(context.Background(), Principal{Name: "运维员", Role: "operator", UserType: "operator"})
	diagnostic, err := service.VehicleSourceDiagnostic(ctx, "LB9A32A24R0LS1426")
	if err != nil {
		t.Fatalf("VehicleSourceDiagnostic returned error: %v", err)
	}
	if diagnostic.Policy.Version != 1 || diagnostic.Access == nil || diagnostic.Evidence.RecommendedLocationProtocol != "JT808" {
		t.Fatalf("diagnostic lost access or policy evidence: %+v", diagnostic)
	}
	if diagnostic.RecommendationReason == "" || diagnostic.RefreshHint == "" {
		t.Fatalf("diagnostic must explain recommendation and refresh boundary: %+v", diagnostic)
	}
	for _, source := range diagnostic.Evidence.LocationSources {
		if len(source.SourceRef) != 64 || source.SelectionReason == "" || source.FirstSeenAt == "" {
			t.Fatalf("source diagnostic is incomplete: %+v", source)
		}
		if strings.Contains(source.SourceRef, "jt808") || strings.Contains(source.SourceRef, "g7") {
			t.Fatalf("source reference must stay opaque: %+v", source)
		}
	}
}

func TestVehicleSourcePolicyUpdateRequiresAdminAndUsesOptimisticVersion(t *testing.T) {
	service := NewService(NewMockStore())
	operator := WithPrincipal(context.Background(), Principal{Name: "运维员", Role: "operator", UserType: "operator"})
	diagnostic, err := service.VehicleSourceDiagnostic(operator, "LB9A32A24R0LS1426")
	if err != nil {
		t.Fatal(err)
	}
	source := diagnostic.Evidence.LocationSources[1]
	if _, err := service.UpdateVehicleSourcePolicy(operator, diagnostic.Evidence.VIN, VehicleSourcePolicyUpdate{
		Version: diagnostic.Policy.Version, SourceRef: source.SourceRef, Enabled: false, Priority: source.Priority, Remark: "人工核验",
	}); err == nil {
		t.Fatal("operator must not modify source policy")
	}
	admin := WithPrincipal(context.Background(), Principal{Name: "平台管理员", Role: "admin", UserType: "admin"})
	updated, err := service.UpdateVehicleSourcePolicy(admin, diagnostic.Evidence.VIN, VehicleSourcePolicyUpdate{
		Version: diagnostic.Policy.Version, SourceRef: source.SourceRef, Enabled: false, Priority: source.Priority, Remark: "人工核验",
	})
	if err != nil {
		t.Fatalf("admin source policy update failed: %v", err)
	}
	if updated.Policy.Version != 2 || len(updated.Policy.Audit) != 1 || updated.Policy.Audit[0].Actor != "平台管理员" {
		t.Fatalf("source policy update must version and audit: %+v", updated.Policy)
	}
	if _, err := service.UpdateVehicleSourcePolicy(admin, diagnostic.Evidence.VIN, VehicleSourcePolicyUpdate{
		Version: 1, SourceRef: source.SourceRef, Enabled: true, Priority: source.Priority,
	}); err == nil {
		t.Fatal("stale source policy update must conflict")
	}
}

func (s *countingStore) VehicleServiceOverviews(ctx context.Context, query VehicleOverviewBatchQuery) (Page[VehicleServiceOverview], error) {
	s.overviewBatchCalls++
	return s.MockStore.VehicleServiceOverviews(ctx, query)
}

func TestVehicleServiceOverviewUsesBatchDataPath(t *testing.T) {
	store := newCountingStore()
	service := NewService(store)
	overview, err := service.VehicleServiceOverview(context.Background(), "粤AG18312", "")
	if err != nil {
		t.Fatalf("VehicleServiceOverview returned error: %v", err)
	}
	if overview.VIN != "LB9A32A24R0LS1426" {
		t.Fatalf("expected canonical VIN from overview, got %+v", overview)
	}
	if store.overviewBatchCalls != 1 || store.vehiclesCalls != 0 || store.vehicleRealtimeCalls != 0 {
		t.Fatalf("single overview should use one batch store call, batch=%d vehicles=%d realtime=%d", store.overviewBatchCalls, store.vehiclesCalls, store.vehicleRealtimeCalls)
	}
}

func TestVehicleServiceOverviewsUsesBatchDataPath(t *testing.T) {
	store := newCountingStore()
	service := NewService(store)
	page, err := service.VehicleServiceOverviews(context.Background(), VehicleOverviewBatchQuery{
		Keywords: []string{"粤AG18312", "LMRKH9AC2R1004087"},
		Limit:    200,
	})
	if err != nil {
		t.Fatalf("VehicleServiceOverviews returned error: %v", err)
	}
	if page.Total != 2 || len(page.Items) != 2 {
		t.Fatalf("expected two overview rows, got %+v", page)
	}
	if store.vehiclesCalls > 1 || store.vehicleRealtimeCalls > 1 {
		t.Fatalf("batch overview should avoid per-keyword store calls, vehicles=%d realtime=%d", store.vehiclesCalls, store.vehicleRealtimeCalls)
	}
}

func TestVehicleServiceSummaryCountsProtocolOnlineByProtocolSlot(t *testing.T) {
	service := NewService(NewMockStore())
	summary, err := service.VehicleServiceSummary(context.Background())
	if err != nil {
		t.Fatalf("VehicleServiceSummary returned error: %v", err)
	}
	byProtocol := map[string]ProtocolStat{}
	for _, protocol := range summary.Protocols {
		byProtocol[protocol.Protocol] = protocol
	}
	gb32960 := byProtocol["GB32960"]
	if gb32960.Total != 2 {
		t.Fatalf("expected two GB32960 source slots, got %+v", gb32960)
	}
	if gb32960.Online != 1 {
		t.Fatalf("GB32960 online count must use GB32960 source status only, got %+v", gb32960)
	}
}

func TestCompleteProtocolStatsIncludesCanonicalSlots(t *testing.T) {
	stats := completeProtocolStats([]ProtocolStat{{Protocol: "JT808", Online: 2, Total: 5}})
	byProtocol := map[string]ProtocolStat{}
	for _, stat := range stats {
		byProtocol[stat.Protocol] = stat
	}
	for _, protocol := range canonicalVehicleProtocols {
		if _, ok := byProtocol[protocol]; !ok {
			t.Fatalf("canonical protocol %s should be present, got %+v", protocol, stats)
		}
	}
	if byProtocol["JT808"].Online != 2 || byProtocol["JT808"].Total != 5 {
		t.Fatalf("existing protocol stats should be preserved, got %+v", byProtocol["JT808"])
	}
	if byProtocol["GB32960"].Online != 0 || byProtocol["GB32960"].Total != 0 {
		t.Fatalf("missing canonical protocol should be exposed as zero slot, got %+v", byProtocol["GB32960"])
	}
}

func TestSummarizeTrackUsesMileageAndChronology(t *testing.T) {
	points := []HistoryLocationRow{
		{DeviceTime: "2026-07-03 10:00:00", TotalMileageKm: 100, SpeedKmh: 0, Longitude: 113.1, Latitude: 23.1},
		{DeviceTime: "2026-07-03 10:30:00", TotalMileageKm: 112.5, SpeedKmh: 50, Longitude: 113.2, Latitude: 23.2},
	}
	summary := summarizeTrack(points)
	if summary.DistanceKm != 12.5 || summary.DurationSeconds != 1800 || summary.MaximumSpeedKmh != 50 {
		t.Fatalf("unexpected track summary: %+v", summary)
	}
}

func TestTrackEventKeepsSynchronizedTelemetryEvidence(t *testing.T) {
	direction := int64(88)
	alarm := int64(2)
	event := trackEvent(7, "alarm", "报警点", HistoryLocationRow{DeviceTime: "2026-07-03 10:00:00", SpeedKmh: 36, SOCPercent: 78.5, SOCAvailable: true, DirectionDeg: &direction, AlarmFlag: &alarm, Longitude: 113.2, Latitude: 23.1})
	if event.Index != 7 || !event.SOCAvailable || event.SOCPercent != 78.5 || event.DirectionDeg == nil || *event.DirectionDeg != 88 || event.AlarmFlag == nil || *event.AlarmFlag != 2 {
		t.Fatalf("track event lost synchronized telemetry evidence: %+v", event)
	}
}

func TestSampleTrackPointsPreservesEndpoints(t *testing.T) {
	points := make([]HistoryLocationRow, 10)
	for index := range points {
		points[index].DeviceTime = strconv.Itoa(index)
	}
	sampled := sampleTrackPoints(points, 4)
	if len(sampled) != 4 || sampled[0].DeviceTime != "0" || sampled[len(sampled)-1].DeviceTime != "9" {
		t.Fatalf("sampling should preserve endpoints: %+v", sampled)
	}
}

func TestTrackAnalysisFiltersInvalidDuplicateAndDriftPoints(t *testing.T) {
	points := []HistoryLocationRow{
		{DeviceTime: "2026-07-03 10:00:00", Protocol: "JT808", Longitude: 113.1, Latitude: 23.1},
		{DeviceTime: "2026-07-03 10:00:00", Protocol: "JT808", Longitude: 113.1, Latitude: 23.1},
		{DeviceTime: "2026-07-03 10:01:00", Protocol: "JT808", Longitude: 114.1, Latitude: 24.1},
		{DeviceTime: "2026-07-03 10:02:00", Protocol: "JT808", Longitude: 0, Latitude: 0},
		{DeviceTime: "2026-07-03 10:03:00", Protocol: "JT808", Longitude: 113.101, Latitude: 23.101},
	}
	clean, quality := analyzeTrackPoints(points)
	if len(clean) != 2 || quality.ValidPoints != 2 || quality.DuplicatePoints != 1 || quality.DriftPoints != 1 || quality.InvalidCoordinatePoints != 1 {
		t.Fatalf("unexpected track quality projection: clean=%+v quality=%+v", clean, quality)
	}
	if quality.Status != "warning" {
		t.Fatalf("filtered evidence must surface as warning: %+v", quality)
	}
}

func TestTrackPrimarySourcePreventsParallelProtocolsFromBecomingOneRoute(t *testing.T) {
	points := []HistoryLocationRow{
		{DeviceTime: "2026-07-03 10:00:00", Protocol: "GB32960", Longitude: 113.1, Latitude: 23.1},
		{DeviceTime: "2026-07-03 10:00:00", Protocol: "JT808", Longitude: 103.1, Latitude: 30.1},
		{DeviceTime: "2026-07-03 10:01:00", Protocol: "GB32960", Longitude: 113.101, Latitude: 23.101},
		{DeviceTime: "2026-07-03 10:01:00", Protocol: "JT808", Longitude: 103.101, Latitude: 30.101},
		{DeviceTime: "2026-07-03 10:02:00", Protocol: "JT808", Longitude: 103.102, Latitude: 30.102},
	}
	clean, quality := analyzeTrackPoints(points)
	if len(clean) != 5 || quality.DriftPoints != 0 {
		t.Fatalf("parallel protocols must be checked within source: clean=%+v quality=%+v", clean, quality)
	}
	selected, protocol, alternate := selectTrackPrimarySource(clean, "")
	if protocol != "JT808" || len(selected) != 3 || alternate != 2 {
		t.Fatalf("unexpected primary source projection: protocol=%s selected=%+v alternate=%d", protocol, selected, alternate)
	}
	explicit, protocol, alternate := selectTrackPrimarySource(clean, "GB32960")
	if protocol != "GB32960" || len(explicit) != 2 || alternate != 3 {
		t.Fatalf("explicit source must win: protocol=%s selected=%+v alternate=%d", protocol, explicit, alternate)
	}
}

func TestTrackSegmentsExposeStopsAndDataGapsWithoutClaimingIgnition(t *testing.T) {
	points := []HistoryLocationRow{
		{DeviceTime: "2026-07-03 10:00:00", SpeedKmh: 0, Longitude: 113.1, Latitude: 23.1},
		{DeviceTime: "2026-07-03 10:02:00", SpeedKmh: 0, Longitude: 113.10001, Latitude: 23.10001},
		{DeviceTime: "2026-07-03 10:04:00", SpeedKmh: 0, Longitude: 113.10002, Latitude: 23.10002},
		{DeviceTime: "2026-07-03 10:05:00", SpeedKmh: 30, Longitude: 113.11, Latitude: 23.11},
		{DeviceTime: "2026-07-03 10:20:00", SpeedKmh: 30, Longitude: 113.12, Latitude: 23.12},
	}
	segments := buildTrackSegments(points)
	if len(segments) != 3 || segments[0].Type != "stopped" || segments[1].Type != "moving" || segments[2].Type != "gap" {
		t.Fatalf("unexpected inferred segments: %+v", segments)
	}
	stops := buildTrackStops(points, segments)
	if len(stops) != 1 || stops[0].DurationSeconds != 240 || !strings.Contains(stops[0].Evidence, "不代表点火状态") {
		t.Fatalf("unexpected inferred stops: %+v", stops)
	}
}

func TestTrackSegmentsDoNotTreatSubsecondSamplesAsDataGaps(t *testing.T) {
	points := []HistoryLocationRow{
		{DeviceTime: "2026-07-03T10:00:00.100+08:00", SpeedKmh: 30, Longitude: 113.1, Latitude: 23.1},
		{DeviceTime: "2026-07-03T10:00:00.900+08:00", SpeedKmh: 31, Longitude: 113.1001, Latitude: 23.1001},
		{DeviceTime: "2026-07-03T10:00:01.700+08:00", SpeedKmh: 32, Longitude: 113.1002, Latitude: 23.1002},
	}
	segments := buildTrackSegments(points)
	if len(segments) != 1 || segments[0].Type != "moving" || segments[0].PointCount != 3 {
		t.Fatalf("subsecond telemetry must remain a continuous moving segment: %+v", segments)
	}
}

func TestMonitorBoundsAreValidatedAndApplied(t *testing.T) {
	bounds, ok, err := parseMonitorBounds("103,29,105,31")
	if err != nil || !ok || !bounds.contains(104, 30) || bounds.contains(106, 30) {
		t.Fatalf("unexpected monitor bounds: bounds=%+v ok=%t err=%v", bounds, ok, err)
	}
	for _, invalid := range []string{"103,29,105", "east,29,105,31", "105,31,103,29", "-181,0,1,1"} {
		if _, _, invalidErr := parseMonitorBounds(invalid); invalidErr == nil {
			t.Fatalf("invalid monitor bounds must be rejected: %q", invalid)
		}
	}
}

func TestMonitorQueryKeepsServerOwnedKeywordAndMotionStatus(t *testing.T) {
	query := normalizeMonitorQuery(url.Values{"keyword": {"沪A"}, "status": {"driving"}})
	if query.Get("vin") != "沪A" || query.Get("limit") != "10000" {
		t.Fatalf("monitor query did not normalize keyword and bound: %v", query)
	}
	if !matchesMonitorStatus(VehicleRealtimeRow{Online: true, SpeedKmh: 20, LastSeen: "2026-07-14T08:00:00+08:00"}, "driving") {
		t.Fatal("driving row must match driving filter")
	}
	if matchesMonitorStatus(VehicleRealtimeRow{Online: true, SpeedKmh: 0, LastSeen: "2026-07-14T08:00:00+08:00"}, "driving") {
		t.Fatal("idle row must not match driving filter")
	}
}

func TestMonitorMapTenThousandVehiclesNeverReturnsTenThousandPoints(t *testing.T) {
	vehicles := syntheticMonitorVehicles(10_000)
	result, err := buildMonitorMapResponse(vehicles, url.Values{"zoom": {"13"}})
	if err != nil {
		t.Fatal(err)
	}
	if (result.Mode != "clusters" && result.Mode != "mixed") || len(result.Clusters) == 0 || len(result.Points)+len(result.Clusters) > monitorPointLimit {
		t.Fatalf("10k unbounded map must stay aggregated: mode=%s points=%d clusters=%d", result.Mode, len(result.Points), len(result.Clusters))
	}
	for _, cluster := range result.Clusters {
		if cluster.Count < 2 {
			t.Fatalf("singleton must be released as a point instead of a fake cluster: %+v", cluster)
		}
	}
	viewport, err := buildMonitorMapResponse(vehicles, url.Values{"zoom": {"11"}, "bounds": {"113,22,114,24"}})
	if err != nil {
		t.Fatal(err)
	}
	if viewport.Mode != "points" || len(viewport.Points) >= monitorPointLimit {
		t.Fatalf("bounded high zoom should return a controlled point set: mode=%s points=%d", viewport.Mode, len(viewport.Points))
	}
}

func TestMonitorWorkspaceSharesOneRealtimeSnapshot(t *testing.T) {
	store := newCountingStore()
	service := NewService(store)
	workspace, err := service.MonitorWorkspace(context.Background(), url.Values{
		"zoom": {"13"}, "railLimit": {"2"},
	})
	if err != nil {
		t.Fatalf("MonitorWorkspace returned error: %v", err)
	}
	if store.vehicleRealtimeCalls != 1 {
		t.Fatalf("workspace should read one realtime snapshot, calls=%d", store.vehicleRealtimeCalls)
	}
	if workspace.Summary.TotalVehicles == 0 || workspace.Map.Total == 0 {
		t.Fatalf("workspace should derive summary and map from the shared snapshot: %+v", workspace)
	}
	if len(workspace.Vehicles.Items) != 2 || workspace.Vehicles.Limit != 2 || workspace.Vehicles.Total < 2 {
		t.Fatalf("workspace rail page should honor its bounded limit: %+v", workspace.Vehicles)
	}
}

func TestMonitorMapUsesMeaningfulClustersAndReleasesPointsAtDetailZoom(t *testing.T) {
	reportIntervalMs := int64(30000)
	vehicles := Page[VehicleRealtimeRow]{Items: []VehicleRealtimeRow{
		{VIN: "NEAR-1", Plate: "粤A00001", Online: true, LocationAvailable: true, Longitude: 113.260, Latitude: 23.130, ReportIntervalMs: &reportIntervalMs},
		{VIN: "NEAR-2", Plate: "粤A00002", Online: true, LocationAvailable: true, Longitude: 113.265, Latitude: 23.135},
		{VIN: "SINGLE", Plate: "粤A00003", Online: true, LocationAvailable: true, Longitude: 113.600, Latitude: 23.500},
	}, Total: 3, Limit: 3}

	mixed, err := buildMonitorMapResponse(vehicles, url.Values{"zoom": {"10"}})
	if err != nil {
		t.Fatal(err)
	}
	if mixed.Mode != "mixed" || len(mixed.Clusters) != 1 || mixed.Clusters[0].Count != 2 || len(mixed.Points) != 1 || mixed.Points[0].VIN != "SINGLE" {
		t.Fatalf("zoom below the detail threshold must keep only a meaningful cluster and release its singleton: %+v", mixed)
	}

	detail, err := buildMonitorMapResponse(vehicles, url.Values{"zoom": {"11"}})
	if err != nil {
		t.Fatal(err)
	}
	if detail.Mode != "points" || len(detail.Points) != 3 || len(detail.Clusters) != 0 {
		t.Fatalf("zoom 11 must release controlled data as exact points: %+v", detail)
	}
	if detail.Points[0].ReportIntervalMs == nil || *detail.Points[0].ReportIntervalMs != reportIntervalMs {
		t.Fatalf("map point must preserve the primary protocol report interval: %+v", detail.Points[0])
	}
}

func BenchmarkMonitorMapTenThousandVehicles(b *testing.B) {
	vehicles := syntheticMonitorVehicles(10_000)
	query := url.Values{"zoom": {"5"}}
	b.ReportAllocs()
	b.ResetTimer()
	for index := 0; index < b.N; index++ {
		if _, err := buildMonitorMapResponse(vehicles, query); err != nil {
			b.Fatal(err)
		}
	}
}

func syntheticMonitorVehicles(count int) Page[VehicleRealtimeRow] {
	items := make([]VehicleRealtimeRow, count)
	for index := range items {
		items[index] = VehicleRealtimeRow{
			VIN: "SYNTH" + strconv.Itoa(index), Online: true, SpeedKmh: float64(index % 90),
			LocationAvailable: true, Longitude: 73 + float64((index/100)%200)*0.3, Latitude: 18 + float64(index%100)*0.3,
			LastSeen: "2026-07-14T08:00:00+08:00",
		}
	}
	return Page[VehicleRealtimeRow]{Items: items, Total: count, Limit: count}
}

func TestKeyPointSamplingPreservesEventsAndMapsToReturnedIndexes(t *testing.T) {
	points := make([]HistoryLocationRow, 20)
	for index := range points {
		points[index].DeviceTime = strconv.Itoa(index)
	}
	sampled, indexes := sampleTrackPointsWithKeys(points, 6, []int{5, 11})
	if len(sampled) != 6 || !containsInt(indexes, 0) || !containsInt(indexes, 5) || !containsInt(indexes, 11) || !containsInt(indexes, 19) {
		t.Fatalf("key-point sampling lost evidence: indexes=%+v", indexes)
	}
	mapped := nearestSampledIndex(11, indexes)
	if mapped < 0 || mapped >= len(indexes) || indexes[mapped] != 11 {
		t.Fatalf("event should map to its preserved returned point: mapped=%d indexes=%+v", mapped, indexes)
	}
}

func TestTrackCoverageDoesNotPresentLatestSliceAsCompleteWindow(t *testing.T) {
	coverage := trackCoverage(url.Values{"dateFrom": {"2026-07-01T00:00:00"}}, 71000, 5000, 4990, 1200, true, true)
	if coverage.Complete || coverage.TotalPoints != 71000 || coverage.FetchedPoints != 5000 || len(coverage.LimitReasons) != 3 {
		t.Fatalf("unexpected bounded coverage: %+v", coverage)
	}
	if !strings.Contains(coverage.Evidence, "不代表完整时间窗") {
		t.Fatalf("coverage boundary must be explicit: %+v", coverage)
	}
}

func TestTrackWindowRequiresBoundedOrderedPair(t *testing.T) {
	if err := validateTrackWindow("2026-07-01T00:00", ""); err == nil {
		t.Fatal("one-sided track window must be rejected")
	}
	if err := validateTrackWindow("2026-07-08T00:00", "2026-07-01T00:00"); err == nil {
		t.Fatal("reversed track window must be rejected")
	}
	if err := validateTrackWindow("2026-07-01T00:00", "2026-07-09T00:00"); err == nil {
		t.Fatal("track window over seven days must be rejected")
	}
	if err := validateTrackWindow("2026-07-01T00:00", "2026-07-08T00:00"); err != nil {
		t.Fatalf("seven-day track window should be accepted: %v", err)
	}
	if err := validateTrackWindow("", ""); err != nil {
		t.Fatalf("unbounded latest-slice compatibility mode should remain available: %v", err)
	}
}

func containsInt(values []int, wanted int) bool {
	for _, value := range values {
		if value == wanted {
			return true
		}
	}
	return false
}

func TestHistoryExportRunsAsControlledAsyncJob(t *testing.T) {
	service := NewService(NewMockStore())
	ctx := exportAdminContext()
	job, err := service.CreateHistoryExport(ctx, HistoryExportRequest{Keywords: []string{"川AHTWO1"}, Category: "location", Metrics: []string{"speedKmh"}, Format: "csv"})
	if err != nil {
		t.Fatalf("create export: %v", err)
	}
	deadline := time.Now().Add(2 * time.Second)
	for time.Now().Before(deadline) {
		jobs := service.ListHistoryExports(ctx)
		if len(jobs) > 0 && jobs[0].ID == job.ID && jobs[0].Status == "completed" {
			path, _, fileErr := service.HistoryExportFile(ctx, job.ID)
			if fileErr != nil {
				t.Fatalf("export file: %v", fileErr)
			}
			body, readErr := os.ReadFile(path)
			if readErr != nil {
				t.Fatalf("read export: %v", readErr)
			}
			if len(body) < 3 || string(body[:3]) != "\xEF\xBB\xBF" {
				t.Fatalf("CSV should include UTF-8 BOM")
			}
			if !strings.Contains(string(body), "数据质量,质量原因,证据ID") {
				t.Fatalf("CSV should explain platform quality: %q", body[:min(len(body), 512)])
			}
			return
		}
		time.Sleep(10 * time.Millisecond)
	}
	t.Fatalf("export job did not complete: %+v", service.ListHistoryExports(ctx))
}

func exportCustomerContext(subject, username, vin string) context.Context {
	return WithPrincipal(context.Background(), Principal{
		SubjectID: subject, Name: username, Username: username, Role: "customer", UserType: "customer", AuthProvider: "local",
		CustomerRef: "customer-" + subject, VehicleVINs: []string{vin},
		VehicleGrants: []VehicleGrant{{VIN: vin, ValidFrom: time.Date(2026, 7, 1, 0, 0, 0, 0, time.FixedZone("Asia/Shanghai", 8*60*60))}},
	})
}

func TestHistoryExportsAreOwnerScopedAndPersistAuditMetadata(t *testing.T) {
	dir := t.TempDir()
	service := NewServiceWithRuntime(NewMockStore(), RuntimeInfo{ExportDir: dir})
	vin := "LNXNEGRR7SR318212"
	customerA := exportCustomerContext("101", "customer-a", vin)
	customerB := exportCustomerContext("102", "customer-b", vin)
	job, err := service.CreateHistoryExport(customerA, HistoryExportRequest{
		Keywords: []string{vin}, Category: "location", DateFrom: "2026-07-14T00:00", DateTo: "2026-07-14T06:00", Metrics: []string{"speedKmh"}, Format: "csv",
	})
	if err != nil {
		t.Fatal(err)
	}
	if job.OwnerID != "local:subject:101" || job.OwnerUsername != "customer-a" || job.CustomerRef != "customer-101" || len(job.VehicleScopes) != 1 || job.VehicleScopes[0].VIN != vin {
		t.Fatalf("owner/scope audit metadata missing: %+v", job)
	}
	if jobs := service.ListHistoryExports(customerB); len(jobs) != 0 {
		t.Fatalf("customer B saw customer A export: %+v", jobs)
	}
	if _, _, err := service.HistoryExportFile(customerB, job.ID); err == nil {
		t.Fatal("customer B should not download customer A export")
	}
	if jobs := service.ListHistoryExports(exportAdminContext()); len(jobs) != 1 || jobs[0].ID != job.ID {
		t.Fatalf("admin should audit all jobs: %+v", jobs)
	}
	deadline := time.Now().Add(2 * time.Second)
	for time.Now().Before(deadline) {
		jobs := service.ListHistoryExports(customerA)
		if len(jobs) == 1 && jobs[0].Status == "completed" {
			path, _, fileErr := service.HistoryExportFile(customerA, job.ID)
			if fileErr != nil {
				t.Fatal(fileErr)
			}
			body, readErr := os.ReadFile(path)
			if readErr != nil {
				t.Fatal(readErr)
			}
			for _, evidence := range []string{"导出审计", "customer-a", "customer-101", "车辆 Scope", vin} {
				if !strings.Contains(string(body), evidence) {
					t.Fatalf("CSV missing audit evidence %q: %q", evidence, body[:min(len(body), 800)])
				}
			}
			return
		}
		time.Sleep(10 * time.Millisecond)
	}
	t.Fatalf("customer export did not complete: %+v", service.ListHistoryExports(customerA))
}

type revocableHistoryExportStore struct {
	*MockStore
	active bool
}

func (s *revocableHistoryExportStore) HistoryExportScopeActive(context.Context, HistoryExportJob) (bool, error) {
	return s.active, nil
}

func TestCustomerHistoryExportStopsWhenAuthorizationIsRevoked(t *testing.T) {
	store := &revocableHistoryExportStore{MockStore: NewMockStore(), active: false}
	service := NewServiceWithRuntime(store, RuntimeInfo{ExportDir: t.TempDir()})
	ctx := exportCustomerContext("101", "customer-a", "LNXNEGRR7SR318212")
	job, err := service.CreateHistoryExport(ctx, HistoryExportRequest{
		Keywords: []string{"LNXNEGRR7SR318212"}, Category: "location", DateFrom: "2026-07-14T00:00", DateTo: "2026-07-14T06:00", Metrics: []string{"speedKmh"}, Format: "csv",
	})
	if err != nil {
		t.Fatal(err)
	}
	deadline := time.Now().Add(2 * time.Second)
	for time.Now().Before(deadline) {
		jobs := service.ListHistoryExports(ctx)
		if len(jobs) == 1 && jobs[0].Status == "failed" {
			if !strings.Contains(jobs[0].Error, "授权已变化") {
				t.Fatalf("unexpected revoke evidence: %+v", jobs[0])
			}
			if _, _, fileErr := service.HistoryExportFile(ctx, job.ID); fileErr == nil {
				t.Fatal("revoked customer should not download export")
			}
			return
		}
		time.Sleep(10 * time.Millisecond)
	}
	t.Fatalf("revoked export did not stop: %+v", service.ListHistoryExports(ctx))
}

type largeHistoryExportStore struct {
	*MockStore
	total int64
}

func (s *largeHistoryExportStore) HistoryExportCount(context.Context, HistoryExportStoreQuery) (int64, error) {
	return s.total, nil
}

func (s *largeHistoryExportStore) HistoryExportBatch(_ context.Context, query HistoryExportStoreQuery, cursor HistoryExportCursor, limit int) ([]HistoryDataRow, HistoryExportCursor, error) {
	remaining := int(s.total) - cursor.Offset
	if remaining <= 0 {
		return []HistoryDataRow{}, cursor, nil
	}
	if remaining < limit {
		limit = remaining
	}
	rows := make([]HistoryDataRow, limit)
	for index := range rows {
		number := cursor.Offset + index
		rows[index] = HistoryDataRow{ID: "row-" + strconv.Itoa(number), VIN: query.VIN, Plate: "粤A测试", Protocol: "JT808", DeviceTime: "2026-07-14T00:00:00+08:00", ServerTime: "2026-07-14T00:00:01+08:00", Quality: "normal", Values: map[string]any{"speedKmh": float64(number % 120)}}
	}
	cursor.Offset += len(rows)
	return rows, cursor, nil
}

func TestHistoryExportStreamsBeyondLegacyLimit(t *testing.T) {
	assertLargeHistoryExport(t, 12_345, 10*time.Second)
}

func TestHistoryExportMillionRows(t *testing.T) {
	if os.Getenv("EXPORT_MILLION_TEST") != "1" {
		t.Skip("set EXPORT_MILLION_TEST=1 for the release capacity gate")
	}
	assertLargeHistoryExport(t, 1_000_000, 2*time.Minute)
}

func assertLargeHistoryExport(t *testing.T, total int64, timeout time.Duration) {
	t.Helper()
	dir := t.TempDir()
	store := &largeHistoryExportStore{MockStore: NewMockStore(), total: total}
	service := NewServiceWithRuntime(store, RuntimeInfo{ExportDir: dir})
	ctx := exportAdminContext()
	job, err := service.CreateHistoryExport(ctx, HistoryExportRequest{Keywords: []string{"LNXNEGRR7SR318212"}, Category: "location", DateFrom: "2026-07-14T00:00", DateTo: "2026-07-14T06:00", Metrics: []string{"speedKmh"}, Format: "csv"})
	if err != nil {
		t.Fatal(err)
	}
	deadline := time.Now().Add(timeout)
	for time.Now().Before(deadline) {
		current := service.ListHistoryExports(ctx)[0]
		if current.Status == "failed" {
			t.Fatalf("export failed: %+v", current)
		}
		if current.Status == "completed" {
			if current.RowCount != int(total) || current.ProcessedRows != total || current.TotalRows != total || current.FileSizeBytes == 0 || current.CompletedAt == "" {
				t.Fatalf("incomplete evidence: %+v", current)
			}
			path, _, fileErr := service.HistoryExportFile(ctx, job.ID)
			if fileErr != nil {
				t.Fatal(fileErr)
			}
			if _, statErr := os.Stat(path + ".part"); !os.IsNotExist(statErr) {
				t.Fatalf("part file should be atomically removed: %v", statErr)
			}
			body, readErr := os.ReadFile(path)
			if readErr != nil {
				t.Fatal(readErr)
			}
			if !strings.Contains(string(body[:min(len(body), 512)]), "查询开始") || !strings.Contains(string(body[:min(len(body), 512)]), "速度(km/h)") {
				t.Fatalf("CSV metadata missing: %q", body[:min(len(body), 512)])
			}
			return
		}
		time.Sleep(10 * time.Millisecond)
	}
	t.Fatalf("export timed out: %+v", service.ListHistoryExports(ctx))
}

func TestRawMetricMetadataKeepsProtocolAndUnit(t *testing.T) {
	label, unit := rawMetricMetadata("jt808.location.additional.total_mileage_km")
	if label != "总里程 · JT808" || unit != "km" {
		t.Fatalf("unexpected RAW metric metadata: %q %q", label, unit)
	}
}

func TestHistoryQualityReasonsExplainLocationRawAndMileageWarnings(t *testing.T) {
	quality, reason := historyLocationQuality(HistoryLocationRow{Longitude: 0, Latitude: 0, SpeedKmh: 30, DeviceTime: "2026-07-14 09:29:58", ServerTime: "2026-07-14 09:29:59"})
	if quality != "warning" || !strings.Contains(reason, "坐标") {
		t.Fatalf("invalid coordinate should be explained: %s %s", quality, reason)
	}
	quality, reason = historyRawQuality(RawFrameRow{DeviceTime: "2026-07-14 09:29:58", ServerTime: "2026-07-14 09:29:59", ParseStatus: "failed", ParseError: "checksum"})
	if quality != "warning" || !strings.Contains(reason, "checksum") {
		t.Fatalf("parse failure should retain detail: %s %s", quality, reason)
	}
	quality, reason = historyMileageQuality(DailyMileageRow{StartMileageKm: 100, EndMileageKm: 130, DailyMileageKm: 12})
	if quality != "warning" || !strings.Contains(reason, "不一致") {
		t.Fatalf("mileage mismatch should be explained: %s %s", quality, reason)
	}
}

func TestMergeDiscoveredRawMetricsScansFetchedScope(t *testing.T) {
	rows := []HistoryDataRow{
		{Values: map[string]any{"frameType": "realtime"}},
		{Values: map[string]any{"gb32960.vehicle.soc_percent": 88}},
	}
	columns := mergeDiscoveredRawMetrics(historyRawMetrics(), rows)
	found := false
	for _, column := range columns {
		if column.Key == "gb32960.vehicle.soc_percent" {
			found = true
			break
		}
	}
	if !found {
		t.Fatalf("discovered field outside the visible page should remain selectable: %+v", columns)
	}
}

func TestBuildLatestTelemetryResponseUsesCatalogAndNewestSourceEvidence(t *testing.T) {
	now := time.Date(2026, 7, 14, 9, 30, 0, 0, time.FixedZone("Asia/Shanghai", 8*60*60))
	definitions := []MetricDefinition{
		{Key: "speed_kmh", Label: "速度", Description: "车辆最新行驶速度", Unit: "km/h", Category: "driving", ValueType: "numeric", SourceFields: map[string]string{"GB32960": "gb32960.vehicle.speed_kmh"}},
		{Key: "alarm_active", Label: "协议告警位", Unit: "", Category: "safety", ValueType: "boolean", SourceFields: map[string]string{"GB32960": "gb32960.alarm.general_alarm_flag"}},
	}
	frames := []RawFrameRow{
		{ID: "new", VIN: "VIN001", Protocol: "GB32960", DeviceTime: "2026-07-14 09:29:58", ServerTime: "2026-07-14 09:29:59", ParseStatus: "ok", SourceEndpoint: "gateway-a", ParsedFields: map[string]any{"gb32960.vehicle.speed_kmh": 42.5, "gb32960.alarm.general_alarm_flag": float64(1), "vendor.custom_temperature_c": 31.2}},
		{ID: "old", VIN: "VIN001", Protocol: "GB32960", DeviceTime: "2026-07-14 09:20:00", ServerTime: "2026-07-14 09:20:01", ParseStatus: "ok", ParsedFields: map[string]any{"gb32960.vehicle.speed_kmh": 10.0}},
	}
	response := buildLatestTelemetryResponse("VIN001", frames, definitions, now)
	if response.VIN != "VIN001" || response.ScannedFrames != 2 || len(response.Values) != 3 || len(response.Categories) != 3 {
		t.Fatalf("unexpected response: %+v", response)
	}
	byKey := map[string]LatestTelemetryValue{}
	for _, value := range response.Values {
		byKey[value.Key] = value
	}
	if speed := byKey["speed_kmh"]; speed.Value != 42.5 || speed.SourceField != "gb32960.vehicle.speed_kmh" || speed.FrameID != "new" || speed.Quality != "good" || speed.FreshnessSeconds != 1 {
		t.Fatalf("unified speed should use newest frame and evidence: %+v", speed)
	}
	if alarm := byKey["alarm_active"]; alarm.Value != true || alarm.Category != "alarm" {
		t.Fatalf("catalog boolean should be normalized: %+v", alarm)
	}
	if extension := byKey["vendor.custom_temperature_c"]; extension.Category != "extension" || extension.Unit != "℃" || extension.Protocol != "GB32960" {
		t.Fatalf("manufacturer extension should retain source semantics: %+v", extension)
	}
}

func TestLatestTelemetryQualityDistinguishesStaleAndWarnings(t *testing.T) {
	now := time.Date(2026, 7, 14, 9, 30, 0, 0, time.FixedZone("Asia/Shanghai", 8*60*60))
	stale, reason, freshness, delay := latestTelemetryQuality(RawFrameRow{DeviceTime: "2026-07-14 09:19:59", ServerTime: "2026-07-14 09:20:00", ParseStatus: "ok"}, now)
	if stale != "stale" || freshness != 600 || delay == nil || *delay != 1 || !strings.Contains(reason, "超过 5 分钟") {
		t.Fatalf("unexpected stale evidence: %s %s %d %v", stale, reason, freshness, delay)
	}
	warning, reason, _, _ := latestTelemetryQuality(RawFrameRow{ServerTime: "2026-07-14 09:29:59", ParseStatus: "failed", ParseError: "checksum"}, now)
	if warning != "warning" || !strings.Contains(reason, "checksum") {
		t.Fatalf("parse warning should retain reason: %s %s", warning, reason)
	}
}

type latestTelemetryQueryStore struct {
	*MockStore
	mu      sync.Mutex
	queries []RawFrameQuery
}

func (s *latestTelemetryQueryStore) RawFrames(ctx context.Context, query RawFrameQuery) (Page[RawFrameRow], error) {
	s.mu.Lock()
	s.queries = append(s.queries, query)
	s.mu.Unlock()
	return s.MockStore.RawFrames(ctx, query)
}

func TestLatestTelemetryBoundsProtocolReads(t *testing.T) {
	store := &latestTelemetryQueryStore{MockStore: NewMockStore()}
	response, err := NewService(store).LatestTelemetry(t.Context(), "LNXNEGRR7SR318212")
	if err != nil {
		t.Fatal(err)
	}
	if len(response.Values) == 0 || response.ScannedFrames != 1 {
		t.Fatalf("expected one mock GB frame: %+v", response)
	}
	store.mu.Lock()
	defer store.mu.Unlock()
	if len(store.queries) != len(canonicalVehicleProtocols) {
		t.Fatalf("queries = %+v", store.queries)
	}
	protocols := map[string]bool{}
	for _, query := range store.queries {
		protocols[query.Protocol] = true
		if query.VIN != "LNXNEGRR7SR318212" || query.Limit != 5 || !query.IncludeFields || !query.SkipCount {
			t.Fatalf("unbounded latest query: %+v", query)
		}
	}
	for _, protocol := range canonicalVehicleProtocols {
		if !protocols[protocol] {
			t.Fatalf("missing protocol %s: %+v", protocol, store.queries)
		}
	}
}

func BenchmarkLatestTelemetryHundredFrames(b *testing.B) {
	now := time.Date(2026, 7, 14, 9, 30, 0, 0, time.FixedZone("Asia/Shanghai", 8*60*60))
	definitions := metricDefinitions()
	frames := make([]RawFrameRow, 100)
	for frameIndex := range frames {
		fields := make(map[string]any, 50)
		for fieldIndex := 0; fieldIndex < 50; fieldIndex++ {
			fields[fmt.Sprintf("vendor.section_%02d.metric_%02d_voltage_v", fieldIndex/10, fieldIndex)] = float64(frameIndex + fieldIndex)
		}
		fields["gb32960.vehicle.speed_kmh"] = float64(frameIndex)
		frames[frameIndex] = RawFrameRow{ID: fmt.Sprintf("frame-%03d", frameIndex), VIN: "VIN001", Protocol: "GB32960", DeviceTime: "2026-07-14 09:29:58", ServerTime: "2026-07-14 09:29:59", ParseStatus: "ok", ParsedFields: fields}
	}
	b.ReportAllocs()
	b.ResetTimer()
	for range b.N {
		response := buildLatestTelemetryResponse("VIN001", frames, definitions, now)
		if len(response.Values) != 51 {
			b.Fatalf("values = %d", len(response.Values))
		}
	}
}
