export interface ApiEnvelope<T> {
  data: T;
  traceId: string;
  timestamp: number;
}

export interface SessionInfo {
  subjectId?: string;
  name: string;
  username?: string;
  role: 'viewer' | 'operator' | 'admin' | 'customer';
  userType?: 'viewer' | 'operator' | 'admin' | 'customer';
  authProvider?: 'local' | 'legacy-token' | 'disabled' | string;
  menuKeys?: string[];
  vehicleCount?: number;
  customerRef?: string;
  tenantRef?: string;
  authMode: 'disabled' | 'enforce';
}

export interface LoginResponse {
  accessToken: string;
  expiresAt: string;
  session: Omit<SessionInfo, 'authMode'>;
}

export interface AdminUser {
  id: number;
  username: string;
  displayName: string;
  userType: 'admin' | 'customer';
  status: 'enabled' | 'disabled';
  customerRef: string;
  tenantRef: string;
  authProvider: string;
  externalSubject?: string;
  menuKeys: string[];
  vehicleVins: string[];
  vehicles: AdminVehicleGrant[];
  grantHistory: AdminVehicleGrantHistory[];
  lastLoginAt?: string;
  createdAt: string;
  updatedAt: string;
}

export interface AdminVehicleGrant {
  vin: string;
  plate: string;
  validFrom: string;
  validTo?: string;
  sourceSystem: string;
  grantedBy: string;
}

export interface AdminVehicleGrantHistory extends AdminVehicleGrant {
  id: number;
  revokedBy: string;
  createdAt: string;
  updatedAt: string;
}

export interface CustomerVehicleGrantInput {
  vin: string;
  validFrom: string;
  validTo: string;
}

export interface CustomerUserInput {
  username?: string;
  displayName: string;
  password?: string;
  status: 'enabled' | 'disabled';
  customerRef: string;
  tenantRef: string;
  menuKeys: string[];
  vehicleVins?: string[];
  vehicleGrants: CustomerVehicleGrantInput[];
}

export interface MonitorSummary {
  totalVehicles: number;
  locationVehicles?: number;
  noLocationVehicles?: number;
  onlineVehicles: number;
  offlineVehicles: number;
  drivingVehicles: number;
  idleVehicles: number;
  alertVehicles: number;
  unknownVehicles: number;
  activeToday: number;
  frameToday: number;
  alertDataAvailable: boolean;
  truncated: boolean;
  asOf: string;
}

export interface MonitorMapPoint {
  vin: string;
  plate: string;
  protocol: string;
  protocols: string[];
  longitude: number;
  latitude: number;
  speedKmh: number;
  socPercent: number;
  totalMileageKm: number;
  lastSeen: string;
  reportIntervalMs?: number;
  locationSource?: string;
  locationConflict?: boolean;
  status: 'driving' | 'idle' | 'offline' | 'unknown';
}

export interface MonitorMapCluster {
  id: string;
  longitude: number;
  latitude: number;
  count: number;
  online: number;
  offline: number;
  driving: number;
  idle: number;
  unknown: number;
}

export interface MonitorMapResponse {
  mode: 'clusters' | 'mixed' | 'points';
  zoom: number;
  total: number;
  truncated: boolean;
  points: MonitorMapPoint[];
  clusters: MonitorMapCluster[];
  asOf: string;
}

export interface MonitorWorkspaceResponse {
  summary: MonitorSummary;
  vehicles: Page<VehicleRealtimeRow>;
  map: MonitorMapResponse;
}

export interface TrackPlaybackResponse {
  vin: string;
  plate: string;
  points: HistoryLocationRow[];
  events: TrackPlaybackEvent[];
  sources: TrackPlaybackSource[];
  segments: TrackSegment[];
  stops: TrackStop[];
  summary: TrackPlaybackSummary;
  coverage: TrackCoverage;
  quality: TrackQuality;
  total: number;
  truncated: boolean;
  sampled: boolean;
  asOf: string;
}

export interface TrackPlaybackSummary {
  startTime: string;
  endTime: string;
  distanceKm: number;
  durationSeconds: number;
  averageSpeedKmh: number;
  maximumSpeedKmh: number;
  pointCount: number;
  movingSeconds: number;
  stoppedSeconds: number;
  stopCount: number;
  segmentCount: number;
}

export interface TrackPlaybackEvent {
  index: number;
  sampledIndex: number;
  type: 'start' | 'end' | 'stop' | 'acceleration' | 'braking' | 'source_switch' | string;
  title: string;
  time: string;
  speedKmh: number;
  socPercent: number;
  socAvailable: boolean;
  directionDeg?: number;
  alarmFlag?: number;
  longitude: number;
  latitude: number;
}

export interface TrackPlaybackSource {
  protocol: string;
  pointCount: number;
  startTime: string;
  endTime: string;
}

export interface TrackCoverage {
  requestedStart: string;
  requestedEnd: string;
  actualStart: string;
  actualEnd: string;
  totalPoints: number;
  fetchedPoints: number;
  processedPoints: number;
  returnedPoints: number;
  complete: boolean;
  limitReasons: string[];
  evidence: string;
}

export interface TrackSegment {
  index: number;
  type: 'moving' | 'stopped' | 'gap' | 'point' | string;
  title: string;
  startTime: string;
  endTime: string;
  durationSeconds: number;
  distanceKm: number;
  pointCount: number;
  startIndex: number;
  endIndex: number;
  sampledStartIndex: number;
  sampledEndIndex: number;
}

export interface TrackStop {
  index: number;
  startTime: string;
  endTime: string;
  durationSeconds: number;
  pointCount: number;
  longitude: number;
  latitude: number;
  sampledIndex: number;
  evidence: string;
}

export interface TrackQuality {
  status: 'good' | 'warning' | string;
  selectedProtocol: string;
  rawPoints: number;
  validPoints: number;
  alternateSourcePoints: number;
  invalidCoordinatePoints: number;
  duplicatePoints: number;
  driftPoints: number;
  sourceSwitches: number;
  largeGapCount: number;
  maximumGapSeconds: number;
  evidence: string;
}

export interface HistoryMetricCatalog {
  categories: HistoryDataCategory[];
  metrics: HistoryMetricDefinition[];
}

export interface MetricCatalog { metrics: MetricDefinition[]; asOf: string; }
export interface MetricDefinition {
  key: string; label: string; description: string; unit: string; category: string; valueType: 'numeric' | 'boolean';
  protocols: string[]; sourceFields: Record<string, string>; searchable: boolean; chartable: boolean; alertable: boolean;
}

export interface HistoryDataCategory { key: 'location' | 'raw' | 'mileage' | string; label: string; }
export interface HistoryMetricDefinition { key: string; label: string; unit: string; category: string; valueType: string; defaultVisible: boolean; }
export interface HistoryDataRow { id: string; vin: string; plate: string; protocol: string; deviceTime: string; serverTime: string; quality: string; qualityReason: string; evidenceId?: string; values: Record<string, unknown>; }
export interface HistoryDataSummary { resultRows: number; vehicleCount: number; sources: string[]; queryDurationMs: number; }
export interface HistoryDataResponse { category: string; columns: HistoryMetricDefinition[]; rows: HistoryDataRow[]; summary: HistoryDataSummary; total: number; limit: number; offset: number; asOf: string; }
export interface HistorySeriesPoint { time: string; value: number | null; min: number | null; max: number | null; count: number; }
export interface HistorySeries { vin: string; plate: string; protocol: string; metric: string; label: string; unit: string; aggregation: 'avg' | 'last' | string; points: HistorySeriesPoint[]; }
export interface HistorySeriesSummary { rawPointCount: number; bucketCount: number; returnedPointCount: number; seriesCount: number; grainSeconds: number; targetPoints: number; expectedBucketCount: number; missingBucketCount: number; queryDurationMs: number; complete: boolean; evidence: string; }
export interface HistorySeriesResponse { metrics: HistoryMetricDefinition[]; series: HistorySeries[]; summary: HistorySeriesSummary; dateFrom: string; dateTo: string; asOf: string; }

export interface HistoryExportRequest { keywords: string[]; category: string; protocol?: string; dateFrom?: string; dateTo?: string; metrics: string[]; format: 'csv'; }
export interface HistoryExportJob { id: string; name: string; status: 'queued' | 'running' | 'completed' | 'failed'; progress: number; format: string; category: string; protocol?: string; keywords: string[]; metrics?: string[]; vehicleVins?: string[]; dateFrom?: string; dateTo?: string; ownerId?: string; ownerName?: string; ownerUsername?: string; ownerRole?: string; ownerUserType?: string; customerRef?: string; tenantRef?: string; rowCount: number; totalRows: number; processedRows: number; fileSizeBytes: number; error?: string; downloadUrl?: string; createdAt: string; updatedAt: string; completedAt?: string; evidence: string; }

export interface AccessQuery { keyword?: string; protocol?: string; oem?: string; model?: string; provider?: string; firstSeenFrom?: string; firstSeenTo?: string; latestSeenFrom?: string; latestSeenTo?: string; onlineState?: string; delayState?: string; connectionState?: string; limit?: number; offset?: number; }
export interface AccessUnresolvedIdentityQuery { keyword?: string; protocol?: string; limit?: number; offset?: number; }
export interface AccessUnresolvedIdentity {
  id: string; protocol: string; identifierMasked: string; plate: string; manufacturer: string; sourceEndpoint: string;
  firstRegisteredAt: string; latestRegisteredAt: string; latestAuthenticatedAt: string; latestSeenAt: string;
  freshnessSec: number; issueCode: string; recommendedAction: string;
}
export interface AccessVehicleRow {
  vin: string; plate: string; oem: string; model: string; company: string; protocol: string; provider: string; source: string;
  firstSeenAt: string; latestEventAt: string; latestReceivedAt: string; reportIntervalSec: number | null;
  dataDelaySec: number | null; freshnessSec: number | null; onlineState: 'online' | 'offline' | 'never_reported' | 'unknown';
  thresholdSec: number; latestMessageType: string; latestEventId: string; latestError: string; delayAbnormal: boolean;
  firstSeenEvidence: string; firstSeenSource: string; reportIntervalEvidence: string; reportSampleCount: number;
  expectedProtocols: string[]; actualProtocols: string[]; missingProtocols: string[]; protocolStatuses: AccessProtocolStatus[];
  connectionState: 'healthy' | 'degraded' | 'incomplete' | 'offline' | 'not_connected'; expectationEvidence: string;
}
export interface AccessProtocolStatus {
  protocol: string; expected: boolean; connected: boolean; provider: string; firstSeenAt: string; latestEventAt: string;
  latestReceivedAt: string; reportIntervalSec: number | null; dataDelaySec: number | null; freshnessSec: number | null;
  onlineState: AccessVehicleRow['onlineState']; thresholdSec: number; delayAbnormal: boolean;
  firstSeenEvidence: string; reportIntervalEvidence: string;
  longitude?: number; latitude?: number; speedKmh?: number; socPercent?: number; totalMileageKm?: number; dailyMileageKm?: number;
}
export interface AccessDistribution { name: string; total: number; online: number; onlineRate: number; }
export interface AccessSummary {
  totalVehicles: number; onlineVehicles: number; offlineVehicles: number; longOfflineVehicles: number;
  neverReported: number; unknownVehicles: number; delayAbnormal: number; reportedToday: number; onlineRate: number;
  healthyVehicles: number; incompleteVehicles: number; degradedVehicles: number;
  protocols: AccessDistribution[]; oems: AccessDistribution[]; asOf: string; thresholdVersion: number;
}
export interface AccessProtocolThreshold { protocol: string; thresholdSec: number; }
export interface AccessThresholdAudit { version: number; actor: string; changedAt: string; summary: string; }
export interface AccessThresholdConfig {
  version: number; defaultThresholdSec: number; delayThresholdSec: number; longOfflineSec: number;
  protocols: AccessProtocolThreshold[]; updatedBy: string; updatedAt: string; audit: AccessThresholdAudit[];
}
export interface AccessThresholdUpdate {
  version: number; defaultThresholdSec: number; delayThresholdSec: number; longOfflineSec: number;
  protocols: AccessProtocolThreshold[]; actor?: string;
}

export type AlertSeverity = 'critical' | 'major' | 'minor';
export type AlertStatus = 'unprocessed' | 'processing' | 'recovered' | 'closed' | 'ignored';
export interface AlertQuery { keyword?: string; severity?: string; status?: string; ruleId?: string; protocol?: string; dateFrom?: string; dateTo?: string; limit?: number; offset?: number; }
export interface AlertSummary { active: number; unprocessed: number; processing: number; recovered: number; closed: number; ignored: number; unreadNotifications: number; asOf: string; }
export interface AlertAction { id: number; action: string; fromStatus: string; toStatus: string; actor: string; note: string; createdAt: string; }
export interface AlertEvent {
  id: string; ruleId: string; ruleName: string; ruleVersion: number; severity: AlertSeverity; status: AlertStatus;
  vin: string; plate: string; protocol: string; metric: string; operator: string; triggerValue: number; threshold: number; thresholdHigh: number;
  unit: string; durationSec: number; location: string; longitude?: number; latitude?: number; sourceEventId: string;
  eventAt: string; receivedAt: string; triggeredAt: string; recoveredAt: string; handler: string; version: number; actions?: AlertAction[];
}
export interface AlertRule {
  id: string; name: string; description: string; severity: AlertSeverity; valueType: 'numeric' | 'boolean'; metric: string;
  operator: string; threshold: number; thresholdHigh: number; booleanThreshold?: boolean; durationSec: number; recoveryOperator: string;
  recoveryThreshold: number; repeatIntervalSec: number; scopeProtocols: string[]; scopeVins: string[]; scopeOems: string[]; scopeModels: string[]; scopeCompanies: string[];
  notificationChannels: string[]; enabled: boolean; version: number; createdBy: string; updatedBy: string; createdAt: string; updatedAt: string;
}
export interface AlertRuleInput extends Omit<AlertRule, 'createdBy' | 'updatedBy' | 'createdAt' | 'updatedAt'> { actor?: string; }
export interface AlertNotification { id: number; eventId: string; title: string; content: string; severity: AlertSeverity; channel: string; read: boolean; createdAt: string; readAt: string; }

export interface ProtocolStat {
  protocol: string;
  online: number;
  total: number;
}

export interface LinkHealth {
  name: string;
  status: string;
  detail?: string;
}

export interface DashboardSummary {
  onlineVehicles: number;
  activeToday: number;
  frameToday: number;
  issueVehicles: number;
  kafkaLag: number | null;
  protocols: ProtocolStat[];
  serviceStatuses: ServiceStatusStat[];
  linkHealth: LinkHealth[];
}

export interface ServiceStatusStat {
  status: string;
  title: string;
  count: number;
}

export interface VehicleRow {
  vin: string;
  plate: string;
  phone: string;
  oem: string;
  protocol: string;
  online: boolean;
  lastSeen: string;
  locationText: string;
  bindingScore: number;
  serviceStatus?: VehicleServiceStatus;
}

export interface VehicleCoverageRow {
  vin: string;
  plate: string;
  phone: string;
  oem: string;
  protocols: string[];
  missingProtocols: string[];
  sourceStatus: VehicleSourceStatus[];
  sourceCount: number;
  onlineSourceCount: number;
  online: boolean;
  lastSeen: string;
  bindingStatus: string;
  serviceStatus?: VehicleServiceStatus;
  sourceConsistency?: VehicleSourceConsistency;
}

export interface VehicleCoverageSummary {
  totalVehicles: number;
  onlineVehicles: number;
  singleSourceVehicles: number;
  multiSourceVehicles: number;
  noDataVehicles: number;
  unboundVehicles: number;
  archiveIncompleteVehicles: number;
  missingSources: MissingSourceStat[];
  archiveMissingFields: ArchiveMissingFieldStat[];
}

export interface VehicleIdentityResolution {
  lookupKey: string;
  resolved: boolean;
  vin: string;
  plate: string;
  phone: string;
  oem: string;
  protocols: string[];
  online: boolean;
  lastSeen: string;
  serviceStatus?: VehicleServiceStatus;
}

export interface VehicleDetail {
  vin: string;
  lookupKey: string;
  lookupResolved: boolean;
  resolution?: VehicleIdentityResolution;
  identity?: VehicleRow;
  profile?: VehicleProfile;
  realtimeSummary?: VehicleRealtimeRow;
  serviceStatus?: VehicleServiceStatus;
  serviceOverview?: VehicleServiceOverview;
  sourceConsistency?: VehicleSourceConsistency;
  sources: string[];
  sourceStatus: VehicleSourceStatus[];
  realtime: RealtimeLocationRow[];
  history: Page<HistoryLocationRow>;
  raw: Page<RawFrameRow>;
  mileage: Page<DailyMileageRow>;
  quality: Page<QualityIssueRow>;
}

export interface VehicleProfile {
  vin: string;
  modelName: string;
  vehicleType: string;
  companyName: string;
  operationStatus: 'unknown' | 'active' | 'inactive' | 'maintenance' | 'retired';
  accessProvider: string;
  firstAccessAt: string;
  runtimeSeconds: number | null;
  sourceSystem: string;
  sourceVersion: string;
  syncedAt: string;
  version: number;
  updatedBy: string;
  updatedAt: string;
  completeness: number;
  missingFields: string[];
}

export interface VehicleProfileInput {
  modelName: string;
  vehicleType: string;
  companyName: string;
  operationStatus: VehicleProfile['operationStatus'];
  accessProvider: string;
  firstAccessAt: string;
  runtimeSeconds: number | null;
  version: number;
}

export interface VehicleProfileSyncItem {
  vin: string;
  modelName: string;
  vehicleType: string;
  companyName: string;
  operationStatus: VehicleProfile['operationStatus'];
  accessProvider: string;
  firstAccessAt: string;
  runtimeSeconds: number | null;
}

export interface VehicleProfileSyncRequest {
  sourceSystem: string;
  sourceVersion: string;
  conflictPolicy: 'preserve' | 'overwrite';
  dryRun: boolean;
  items: VehicleProfileSyncItem[];
}

export interface VehicleProfileSyncItemResult {
  vin: string;
  status: 'created' | 'updated' | 'unchanged' | 'conflict_source' | 'conflict_source_version' | 'missing_vehicle';
  previousSource?: string;
  previousVersion?: string;
  profileVersion?: number;
}

export interface VehicleProfileSyncResult {
  sourceSystem: string;
  sourceVersion: string;
  dryRun: boolean;
  received: number;
  created: number;
  updated: number;
  unchanged: number;
  conflicted: number;
  missing: number;
  items: VehicleProfileSyncItemResult[];
}

export interface VehicleSourceConsistency {
  sourceCount: number;
  onlineSourceCount: number;
  locatedSourceCount: number;
  missingProtocols: string[];
  mileageDeltaKm: number;
  sourceTimeDeltaSeconds: number;
  scope: string;
  status: string;
  severity: string;
  title: string;
  detail: string;
}

export interface VehicleSourceEvidence {
  vin: string;
  plate: string;
  mileageDate: string;
  recommendedLocationProtocol: string;
  recommendedLocationLabel: string;
  locationConflict: boolean;
  conflictDistanceM?: number;
  locationSources: VehicleLocationSourceEvidence[];
  mileageSources: VehicleMileageSourceEvidence[];
  comparison: VehicleSourceEvidenceComparison;
  asOf: string;
}

export interface VehicleLocationSourceEvidence {
  protocol: string;
  sourceLabel: string;
  terminalLabel: string;
  sourceKind: string;
  sourceRef?: string;
  selectedWithinProtocol: boolean;
  recommended: boolean;
  enabled: boolean;
  priority: number;
  online: boolean;
  qualityStatus: string;
  qualityReason: string;
  longitude?: number;
  latitude?: number;
  speedKmh?: number;
  totalMileageKm?: number;
  socPercent?: number;
  eventTime: string;
  receivedAt: string;
  firstSeenAt?: string;
  reportIntervalSec?: number;
  reportSampleCount?: number;
  selectionReason?: string;
}

export interface VehicleMileageSourceEvidence {
  protocol: string;
  sourceLabel: string;
  terminalLabel: string;
  sourceKind: string;
  selectedWithinProtocol: boolean;
  recommended: boolean;
  enabled: boolean;
  priority: number;
  qualityStatus: string;
  qualityReason: string;
  firstTotalMileageKm?: number;
  latestTotalMileageKm?: number;
  dailyMileageKm?: number;
  sampleCount: number;
  firstEventTime: string;
  latestEventTime: string;
}

export interface VehicleSourceEvidenceComparison {
  locationMaxDistanceM: number;
  totalMileageDeltaKm: number;
  dailyMileageDeltaKm: number;
  reportTimeDeltaSeconds: number;
}

export interface VehicleSourcePolicyAudit {
  version: number;
  protocol: string;
  sourceRef: string;
  sourceLabel: string;
  actor: string;
  changedAt: string;
  summary: string;
}

export interface VehicleSourcePolicyConfig {
  vin: string;
  version: number;
  updatedBy: string;
  updatedAt: string;
  audit: VehicleSourcePolicyAudit[];
}

export interface VehicleSourceDiagnostic {
  evidence: VehicleSourceEvidence;
  access?: AccessVehicleRow;
  policy: VehicleSourcePolicyConfig;
  recommendationReason: string;
  refreshHint: string;
}

export interface VehicleSourcePolicyUpdate {
  version: number;
  sourceRef: string;
  enabled: boolean;
  priority: number;
  remark: string;
}

export interface VehicleServiceOverview {
  vin: string;
  plate: string;
  phone: string;
  oem: string;
  protocols: string[];
  primaryProtocol: string;
  coverageStatus: string;
  sourceCount: number;
  onlineSourceCount: number;
  lastSeen: string;
  realtimeCount: number;
  historyCount: number;
  rawCount: number;
  mileageCount: number;
  qualityIssueCount: number;
  serviceStatus?: VehicleServiceStatus;
  sourceConsistency?: VehicleSourceConsistency;
}

export interface VehicleServiceSummary {
  totalVehicles: number;
  boundVehicles: number;
  onlineVehicles: number;
  singleSourceVehicles: number;
  multiSourceVehicles: number;
  noDataVehicles: number;
  identityRequiredVehicles: number;
  archiveIncompleteVehicles: number;
  serviceStatuses: ServiceStatusStat[];
  protocols: ProtocolStat[];
  missingSources: MissingSourceStat[];
  archiveMissingFields: ArchiveMissingFieldStat[];
}

export interface MissingSourceStat {
  protocol: string;
  count: number;
}

export interface ArchiveMissingFieldStat {
  field: string;
  title: string;
  count: number;
}

export interface VehicleServiceStatus {
  status: string;
  severity: string;
  title: string;
  detail: string;
  sourceCount: number;
  onlineSourceCount: number;
}

export interface VehicleSourceStatus {
  protocol: string;
  online: boolean;
  lastSeen: string;
  hasRealtime: boolean;
  hasHistory: boolean;
  hasRaw: boolean;
  hasMileage: boolean;
}

export interface RealtimeLocationRow {
  vin: string;
  plate: string;
  protocol: string;
  longitude: number;
  latitude: number;
  speedKmh: number;
  socPercent: number;
  totalMileageKm: number;
  lastSeen: string;
}

export interface VehicleRealtimeRow {
  vin: string;
  plate: string;
  phone: string;
  oem: string;
  protocols: string[];
  sourceStatus: VehicleSourceStatus[];
  sourceCount: number;
  onlineSourceCount: number;
  online: boolean;
  bindingStatus: string;
  serviceStatus?: VehicleServiceStatus;
  primaryProtocol: string;
  locationSource?: string;
  locationConflict?: boolean;
  conflictDistanceM?: number;
  locationAvailable?: boolean;
  longitude: number;
  latitude: number;
  speedAvailable?: boolean;
  speedKmh: number;
  socAvailable?: boolean;
  socPercent: number;
  mileageAvailable?: boolean;
  totalMileageKm: number;
  todayMileageAvailable?: boolean;
  todayMileageKm?: number;
  lastSeen: string;
  reportIntervalMs?: number;
}

export interface HistoryLocationRow extends RealtimeLocationRow {
  socAvailable: boolean;
  directionDeg?: number;
  alarmFlag?: number;
  deviceTime: string;
  serverTime: string;
}

export interface RawFrameRow {
  id: string;
  vin: string;
  plate: string;
  protocol: string;
  frameType: string;
  deviceTime: string;
  serverTime: string;
  rawSizeBytes: number;
  parseStatus?: string;
  parseError?: string;
  sourceEndpoint?: string;
  parsedFields?: Record<string, unknown>;
}

export interface LatestTelemetryCategory { key: string; label: string; count: number; }
export interface LatestTelemetryValue {
  key: string; sourceField: string; label: string; description?: string; unit: string; category: string;
  valueType: string; value: unknown; protocol: string; sourceEndpoint?: string; frameId: string;
  deviceTime: string; serverTime: string; quality: 'good' | 'stale' | 'warning'; qualityReason: string;
  freshnessSeconds: number; dataDelaySeconds?: number;
}
export interface LatestTelemetryResponse {
  vin: string; categories: LatestTelemetryCategory[]; values: LatestTelemetryValue[]; asOf: string;
  staleAfterSeconds: number; scannedFrames: number; evidence: string;
}

export interface DailyMileageRow {
  vin: string;
  plate: string;
  date: string;
  startMileageKm: number;
  endMileageKm: number;
  dailyMileageKm: number;
  source: string;
  anomalySeverity?: string;
}

export interface MileageSummary {
  vehicleCount: number;
  recordCount: number;
  sourceCount: number;
  totalMileageKm: number;
  averageMileagePerVin: number;
}

export interface MileageTrendPoint { date: string; mileageKm: number; vehicles: number; }
export interface MileageVehicleRank { vin: string; plate: string; mileageKm: number; latestMileageKm: number; activeDays: number; }
export interface MileageStatistics {
  dateFrom: string; dateTo: string; vehicleCount: number; recordCount: number; sourceCount: number;
  periodMileageKm: number; fleetLatestMileageKm: number; averageMileagePerVin: number; averageDailyMileageKm: number;
  trend: MileageTrendPoint[]; ranking: MileageVehicleRank[]; asOf: string; evidence: string;
}

export interface OnlineStatisticsSummary {
  vehicleCount: number;
  onlineVehicleCount: number;
  offlineVehicleCount: number;
  onlineRatePercent: number;
  redisOnlineKeys?: number | null;
  protocolStats: ProtocolStat[];
  evidence: string;
}

export interface OnlineVehicleStatusRow {
  vin: string;
  plate: string;
  phone: string;
  oem: string;
  protocols: string[];
  online: boolean;
  lastSeen: string;
  offlineDurationMinutes?: number | null;
  sourceCount: number;
  onlineSourceCount: number;
  bindingStatus: string;
}

export interface QualityIssueRow {
  vin: string;
  plate: string;
  phone: string;
  sourceEndpoint: string;
  protocol: string;
  issueType: string;
  severity: string;
  lastSeen: string;
  detail: string;
}

export interface QualitySummary {
  issueVehicleCount: number;
  issueRecordCount: number;
  errorCount: number;
  warningCount: number;
  protocols: QualityBucketStat[];
  issueTypes: QualityBucketStat[];
}

export interface QualityBucketStat {
  name: string;
  count: number;
}

export interface QualityNotificationPlan {
  summary: QualitySummary;
  rules: QualityAlertRule[];
  policies: QualityNotificationPolicy[];
  priorityIssues: QualityPriorityIssue[];
  activeRuleCount: number;
  p0RuleCount: number;
}

export interface QualityAlertRule {
  issueType: string;
  title: string;
  level: string;
  owner: string;
  trigger: string;
  notify: string;
  sla: string;
  count: number;
}

export interface QualityNotificationPolicy {
  name: string;
  target: string;
  channel: string;
  condition: string;
  escalationMinutes: number;
  acceptanceCriteria: string;
}

export interface QualityPriorityIssue extends QualityIssueRow {
  priority: 'P0' | 'P1';
  actionLabel: string;
  actionDetail: string;
  sla: string;
  vehicleLabel: string;
  realtimeHash: string;
  historyHash: string;
  rawHash: string;
  vehicleHash: string;
  notificationText: string;
}

export interface OpsHealth {
  linkHealth: LinkHealth[];
  kafkaLag: number | null;
  activeConnections: number | null;
  capacityMetrics?: CapacityMetrics;
  capacityFindings: string[];
  redisOnlineKeys: number | null;
  tdengineWritable: boolean;
  mysqlWritable: boolean;
  runtime: RuntimeInfo;
}

export interface SourceReadinessPlan {
  totalVehicles: number;
  boundVehicles: number;
  identityRequiredVehicles: number;
  onlineVehicles: number;
  kafkaLag: number | null;
  activeConnections: number | null;
  redisOnlineKeys: number | null;
  platformRelease: string;
  sources: SourceReadinessRow[];
}

export interface SourceReadinessRow {
  protocol: string;
  role: string;
  online: number;
  total: number;
  onlineRate: number;
  missingVehicles: number;
  severity: 'ok' | 'warning' | 'error' | string;
  status: string;
  evidence: string;
  action: string;
  acceptance: string;
  vehiclesHash: string;
  realtimeHash: string;
  historyHash: string;
  alertHash: string;
}

export interface CapacityMetrics {
  activeConnections: number;
  kafkaLag: number;
  bridgeConsumerPending: number;
  bridgeAckPending: number;
  bridgeBatchPendingMessages: number;
  fastWriterConsumerPending: number;
  fastWriterAckPending: number;
  fastWriterBatchPending: number;
  historyBatchPending: number;
  historyRowsPending: number;
}

export interface RuntimeInfo {
  dataMode?: 'mock' | 'production' | string;
  requestTimeoutMs: number;
  amapWebJsConfigured?: boolean;
  amapApiConfigured?: boolean;
  amapSecurityProxyEnabled?: boolean;
  amapSecurityCodeExposed?: boolean;
  amapSecurityServiceHost?: string;
  platformRelease?: string;
}

export interface MapReverseGeocode {
  provider: string;
  longitude: number;
  latitude: number;
  formattedAddress: string;
  province?: string;
  city?: string;
  district?: string;
  township?: string;
  adcode?: string;
}

export interface Page<T> {
  items: T[];
  total: number;
  limit: number;
  offset: number;
}
