package stats

import (
	"context"
	"database/sql"
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"

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

type Queryer interface {
	QueryContext(context.Context, string, ...any) (*sql.Rows, error)
}

type MetricQuery struct {
	VIN          string
	Protocol     string
	IncludeTotal bool
	DateFrom     string
	DateTo       string
	Limit        int
	Offset       int
}

type MetricSourceQuery struct {
	VIN           string
	Protocol      string
	DateFrom      string
	DateTo        string
	SourceIP      string
	QualityStatus string
	QualityReason string
	Selected      *bool
	OrderBy       string
	IncludeTotal  bool
	Limit         int
	Offset        int
}

type MetricDiagnosisQuery struct {
	VIN          string
	Protocol     string
	Date         string
	Diagnosis    string
	Reason       string
	Severity     string
	IncludeTotal bool
	Limit        int
	Offset       int
}

type MetricRow struct {
	VIN                  string   `json:"vin"`
	StatDate             string   `json:"stat_date"`
	Protocol             string   `json:"protocol"`
	SourceID             *int64   `json:"source_id,omitempty"`
	SourceIP             string   `json:"source_ip,omitempty"`
	LatestSourceEndpoint string   `json:"latest_source_endpoint,omitempty"`
	PlatformName         string   `json:"platform_name,omitempty"`
	SourceCode           string   `json:"source_code,omitempty"`
	SourceKind           string   `json:"source_kind,omitempty"`
	DailyMileageKM       float64  `json:"daily_mileage_km"`
	LatestTotalMileageKM *float64 `json:"latest_total_mileage_km,omitempty"`
	UpdatedAt            string   `json:"updated_at"`
}

type MetricSourceRow struct {
	VIN                  string   `json:"vin"`
	StatDate             string   `json:"stat_date"`
	Protocol             string   `json:"protocol"`
	SourceKey            string   `json:"source_key"`
	SourceIP             string   `json:"source_ip"`
	SourceEndpoint       string   `json:"source_endpoint,omitempty"`
	Phone                string   `json:"phone,omitempty"`
	DeviceID             string   `json:"device_id,omitempty"`
	PlatformName         string   `json:"platform_name,omitempty"`
	SourceID             *int64   `json:"source_id,omitempty"`
	SourceCode           string   `json:"source_code,omitempty"`
	SourceKind           string   `json:"source_kind,omitempty"`
	SourceEnabled        *bool    `json:"source_enabled,omitempty"`
	TrustPriority        *int     `json:"trust_priority,omitempty"`
	FirstTotalMileageKM  *float64 `json:"first_total_mileage_km,omitempty"`
	LatestTotalMileageKM *float64 `json:"latest_total_mileage_km,omitempty"`
	DailyMileageKM       float64  `json:"daily_mileage_km"`
	SampleCount          int64    `json:"sample_count"`
	FirstEventTime       string   `json:"first_event_time,omitempty"`
	LatestEventTime      string   `json:"latest_event_time,omitempty"`
	QualityStatus        string   `json:"quality_status"`
	QualityReason        string   `json:"quality_reason,omitempty"`
	IsSelected           bool     `json:"is_selected"`
	SelectionStatus      string   `json:"selection_status,omitempty"`
	SelectionReason      string   `json:"selection_reason,omitempty"`
	SelectionAction      string   `json:"selection_action,omitempty"`
	UpdatedAt            string   `json:"updated_at"`
}

type MetricDiagnosisRow struct {
	VIN                            string   `json:"vin"`
	StatDate                       string   `json:"stat_date"`
	Protocol                       string   `json:"protocol"`
	Plate                          string   `json:"plate,omitempty"`
	PlatformName                   string   `json:"platform_name,omitempty"`
	Peer                           string   `json:"peer,omitempty"`
	SnapshotEventTime              string   `json:"snapshot_event_time,omitempty"`
	LocationEventTime              string   `json:"location_event_time,omitempty"`
	SnapshotUpdatedAt              string   `json:"snapshot_updated_at,omitempty"`
	LocationUpdatedAt              string   `json:"location_updated_at,omitempty"`
	RealtimeTotalMileageKM         *float64 `json:"realtime_total_mileage_km,omitempty"`
	RealtimeTotalMileageAt         string   `json:"realtime_total_mileage_event_time,omitempty"`
	DailyMileageKM                 *float64 `json:"daily_mileage_km,omitempty"`
	DailyLatestTotalMileageKM      *float64 `json:"daily_latest_total_mileage_km,omitempty"`
	SourceSampleCount              int64    `json:"source_sample_count"`
	OKSourceCount                  int64    `json:"ok_source_count"`
	SelectableSourceCount          int64    `json:"selectable_source_count"`
	LatestStatEventTime            string   `json:"latest_stat_event_time,omitempty"`
	QualityStatuses                []string `json:"quality_statuses,omitempty"`
	RealtimeFieldCount             int      `json:"realtime_field_count,omitempty"`
	RealtimeSampleFields           []string `json:"realtime_sample_fields,omitempty"`
	RealtimeMileageFieldStatus     string   `json:"realtime_mileage_field_status,omitempty"`
	RealtimeMileageCandidateFields []string `json:"realtime_mileage_candidate_fields,omitempty"`
	RealtimeMileageEvidence        string   `json:"realtime_mileage_evidence,omitempty"`
	RawFrameQueryPath              string   `json:"raw_frame_query_path,omitempty"`
	SourceSampleQueryPath          string   `json:"source_sample_query_path,omitempty"`
	Diagnosis                      string   `json:"diagnosis"`
	Reason                         string   `json:"reason"`
	Severity                       string   `json:"severity,omitempty"`
	RecommendedOperatorAction      string   `json:"recommended_operator_action,omitempty"`
}

type MetricDiagnosisSummaryRow struct {
	StatDate             string `json:"stat_date"`
	Protocol             string `json:"protocol"`
	ActiveCount          int64  `json:"active_count"`
	OKCount              int64  `json:"ok_count"`
	MissingDailyCount    int64  `json:"missing_daily_count"`
	NoSourceSampleCount  int64  `json:"no_source_sample_count"`
	NoTotalMileageCount  int64  `json:"no_total_mileage_count"`
	ActionableIssueCount int64  `json:"actionable_issue_count"`
}

type MetricDiagnosisReasonSummaryRow struct {
	StatDate                  string `json:"stat_date"`
	Protocol                  string `json:"protocol"`
	Diagnosis                 string `json:"diagnosis"`
	Reason                    string `json:"reason"`
	Count                     int64  `json:"count"`
	Severity                  string `json:"severity,omitempty"`
	RecommendedOperatorAction string `json:"recommended_operator_action,omitempty"`
}

type MetricDiagnosisFieldStatusSummaryRow struct {
	StatDate                   string `json:"stat_date"`
	Protocol                   string `json:"protocol"`
	Diagnosis                  string `json:"diagnosis"`
	Reason                     string `json:"reason"`
	RealtimeMileageFieldStatus string `json:"realtime_mileage_field_status"`
	Count                      int64  `json:"count"`
	Severity                   string `json:"severity,omitempty"`
	FieldStatusSeverity        string `json:"field_status_severity,omitempty"`
	RecommendedOperatorAction  string `json:"recommended_operator_action,omitempty"`
	FieldStatusAction          string `json:"field_status_action,omitempty"`
}

type MetricSourceQualitySummaryRow struct {
	StatDate       string  `json:"stat_date"`
	Protocol       string  `json:"protocol"`
	QualityStatus  string  `json:"quality_status"`
	QualityReason  string  `json:"quality_reason,omitempty"`
	SourceCount    int64   `json:"source_count"`
	VehicleCount   int64   `json:"vehicle_count"`
	SelectedCount  int64   `json:"selected_count"`
	SampleCount    int64   `json:"sample_count"`
	DailyMileageKM float64 `json:"daily_mileage_km"`
}

type MetricSourceSelectionSummaryRow struct {
	StatDate        string  `json:"stat_date"`
	Protocol        string  `json:"protocol"`
	SelectionStatus string  `json:"selection_status"`
	SelectionReason string  `json:"selection_reason"`
	SelectionAction string  `json:"selection_action,omitempty"`
	SourceCount     int64   `json:"source_count"`
	VehicleCount    int64   `json:"vehicle_count"`
	SelectedCount   int64   `json:"selected_count"`
	SampleCount     int64   `json:"sample_count"`
	DailyMileageKM  float64 `json:"daily_mileage_km"`
}

type MetricRepository struct {
	db Queryer
}

const (
	metricDiagnosisSeverityOK            = "ok"
	metricDiagnosisSeverityPipeline      = "pipeline"
	metricDiagnosisSeveritySourceData    = "source_data"
	metricDiagnosisRealtimeFieldLimit    = 80
	metricDiagnosisMileageCandidateLimit = 20
)

func NewMetricRepository(db Queryer) *MetricRepository {
	if db == nil {
		panic("metric query db must not be nil")
	}
	return &MetricRepository{db: db}
}

func (r *MetricRepository) Query(ctx context.Context, query MetricQuery) ([]MetricRow, error) {
	query = normalizeMetricQuery(query)
	sqlText, args := buildMetricSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricRow, 0)
	for rows.Next() {
		var row MetricRow
		var statDate scanDate
		var updatedAt scanDateTime
		var sourceID sql.NullInt64
		var sourceIP, endpoint, platform, sourceCode, sourceKind sql.NullString
		var latest sql.NullFloat64
		if err := rows.Scan(
			&row.VIN,
			&statDate,
			&row.Protocol,
			&sourceID,
			&sourceIP,
			&endpoint,
			&platform,
			&sourceCode,
			&sourceKind,
			&row.DailyMileageKM,
			&latest,
			&updatedAt,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.UpdatedAt = updatedAt.String
		if sourceID.Valid {
			row.SourceID = &sourceID.Int64
		}
		row.SourceIP = sourceIP.String
		row.LatestSourceEndpoint = endpoint.String
		row.PlatformName = platform.String
		row.SourceCode = sourceCode.String
		row.SourceKind = normalizeSourceKindForRead(sourceKind.String)
		if !sourceKind.Valid {
			row.SourceKind = ""
		}
		if latest.Valid {
			row.LatestTotalMileageKM = &latest.Float64
		}
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) QuerySources(ctx context.Context, query MetricSourceQuery) ([]MetricSourceRow, error) {
	query = normalizeMetricSourceQuery(query)
	sqlText, args := buildMetricSourceSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricSourceRow, 0)
	for rows.Next() {
		var row MetricSourceRow
		var statDate scanDate
		var endpoint, phone, deviceID, platform, sourceCode, sourceKind, qualityReason sql.NullString
		var sourceID, enabled, trustPriority sql.NullInt64
		var firstTotal, latestTotal sql.NullFloat64
		var firstEvent, latestEvent, updatedAt scanDateTime
		var selected int
		if err := rows.Scan(
			&row.VIN,
			&statDate,
			&row.Protocol,
			&row.SourceKey,
			&row.SourceIP,
			&endpoint,
			&phone,
			&deviceID,
			&platform,
			&sourceID,
			&sourceCode,
			&sourceKind,
			&enabled,
			&trustPriority,
			&firstTotal,
			&latestTotal,
			&row.DailyMileageKM,
			&row.SampleCount,
			&firstEvent,
			&latestEvent,
			&row.QualityStatus,
			&qualityReason,
			&selected,
			&updatedAt,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.SourceEndpoint = endpoint.String
		row.Phone = phone.String
		row.DeviceID = deviceID.String
		row.PlatformName = platform.String
		if sourceID.Valid {
			row.SourceID = &sourceID.Int64
		}
		row.SourceCode = sourceCode.String
		row.SourceKind = normalizeSourceKindForRead(sourceKind.String)
		if !sourceKind.Valid {
			row.SourceKind = ""
		}
		if enabled.Valid {
			value := enabled.Int64 != 0
			row.SourceEnabled = &value
		}
		if trustPriority.Valid {
			value := int(trustPriority.Int64)
			row.TrustPriority = &value
		}
		if firstTotal.Valid {
			row.FirstTotalMileageKM = &firstTotal.Float64
		}
		if latestTotal.Valid {
			row.LatestTotalMileageKM = &latestTotal.Float64
		}
		row.FirstEventTime = firstEvent.String
		row.LatestEventTime = latestEvent.String
		row.QualityReason = qualityReason.String
		row.IsSelected = selected != 0
		row.SelectionStatus, row.SelectionReason, row.SelectionAction = metricSourceSelectionEvidence(row)
		row.UpdatedAt = updatedAt.String
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) QueryDiagnostics(ctx context.Context, query MetricDiagnosisQuery) ([]MetricDiagnosisRow, error) {
	query = normalizeMetricDiagnosisQuery(query)
	sqlText, args := buildMetricDiagnosisSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricDiagnosisRow, 0)
	for rows.Next() {
		var row MetricDiagnosisRow
		var statDate scanDate
		var snapshotEvent, locationEvent, snapshotUpdated, locationUpdated, mileageAt, latestStatEvent scanDateTime
		var plate, platform, peer, qualityStatuses, snapshotParsedJSON sql.NullString
		var realtimeTotal, dailyMileage, dailyLatestTotal sql.NullFloat64
		if err := rows.Scan(
			&row.VIN,
			&statDate,
			&row.Protocol,
			&plate,
			&platform,
			&peer,
			&snapshotEvent,
			&locationEvent,
			&snapshotUpdated,
			&locationUpdated,
			&realtimeTotal,
			&mileageAt,
			&dailyMileage,
			&dailyLatestTotal,
			&row.SourceSampleCount,
			&row.OKSourceCount,
			&row.SelectableSourceCount,
			&latestStatEvent,
			&qualityStatuses,
			&snapshotParsedJSON,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.Plate = plate.String
		row.PlatformName = platform.String
		row.Peer = peer.String
		row.SnapshotEventTime = snapshotEvent.String
		row.LocationEventTime = locationEvent.String
		row.SnapshotUpdatedAt = snapshotUpdated.String
		row.LocationUpdatedAt = locationUpdated.String
		row.RealtimeTotalMileageAt = mileageAt.String
		row.LatestStatEventTime = latestStatEvent.String
		if realtimeTotal.Valid {
			row.RealtimeTotalMileageKM = &realtimeTotal.Float64
		}
		if dailyMileage.Valid {
			row.DailyMileageKM = &dailyMileage.Float64
		}
		if dailyLatestTotal.Valid {
			row.DailyLatestTotalMileageKM = &dailyLatestTotal.Float64
		}
		row.QualityStatuses = splitMetricStatuses(qualityStatuses.String)
		row.RealtimeFieldCount, row.RealtimeSampleFields = metricDiagnosisRealtimeFields(snapshotParsedJSON.String, metricDiagnosisRealtimeFieldLimit)
		row.Diagnosis, row.Reason = classifyMetricDiagnosis(dailyMileage.Valid, row.SourceSampleCount, row.OKSourceCount, row.SelectableSourceCount, realtimeTotal, row.RealtimeTotalMileageAt, row.StatDate)
		row.Severity = metricDiagnosisIssueSeverity(row.Diagnosis, row.Reason)
		row.RealtimeMileageFieldStatus, row.RealtimeMileageCandidateFields, row.RealtimeMileageEvidence = metricDiagnosisRealtimeMileageEvidence(
			row.Protocol,
			row.Diagnosis,
			row.Reason,
			row.RealtimeFieldCount,
			row.RealtimeSampleFields,
			snapshotParsedJSON.String,
			row.RealtimeTotalMileageKM,
			row.RealtimeTotalMileageAt,
			row.StatDate,
		)
		row.RawFrameQueryPath = metricDiagnosisRawFrameQueryPath(row)
		row.SourceSampleQueryPath = metricDiagnosisSourceSampleQueryPath(row)
		row.RecommendedOperatorAction = metricDiagnosisRecommendedOperatorAction(row.Diagnosis, row.Reason)
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) QueryDiagnosisSummary(ctx context.Context, query MetricDiagnosisQuery) ([]MetricDiagnosisSummaryRow, error) {
	query = normalizeMetricDiagnosisQuery(query)
	query.Diagnosis = ""
	sqlText, args := buildMetricDiagnosisSummarySQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricDiagnosisSummaryRow, 0)
	for rows.Next() {
		var row MetricDiagnosisSummaryRow
		var statDate scanDate
		if err := rows.Scan(
			&statDate,
			&row.Protocol,
			&row.ActiveCount,
			&row.OKCount,
			&row.MissingDailyCount,
			&row.NoSourceSampleCount,
			&row.NoTotalMileageCount,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.ActionableIssueCount = row.MissingDailyCount + row.NoSourceSampleCount + row.NoTotalMileageCount
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) QueryDiagnosisReasonSummary(ctx context.Context, query MetricDiagnosisQuery) ([]MetricDiagnosisReasonSummaryRow, error) {
	query = normalizeMetricDiagnosisQuery(query)
	sqlText, args := buildMetricDiagnosisReasonSummarySQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricDiagnosisReasonSummaryRow, 0)
	for rows.Next() {
		var row MetricDiagnosisReasonSummaryRow
		var statDate scanDate
		if err := rows.Scan(
			&statDate,
			&row.Protocol,
			&row.Diagnosis,
			&row.Reason,
			&row.Count,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.Severity = metricDiagnosisIssueSeverity(row.Diagnosis, row.Reason)
		row.RecommendedOperatorAction = metricDiagnosisRecommendedOperatorAction(row.Diagnosis, row.Reason)
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) QueryDiagnosisFieldStatusSummary(ctx context.Context, query MetricDiagnosisQuery) ([]MetricDiagnosisFieldStatusSummaryRow, error) {
	query = normalizeMetricDiagnosisQuery(query)
	sqlText, args := buildMetricDiagnosisFieldStatusSummarySQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricDiagnosisFieldStatusSummaryRow, 0)
	for rows.Next() {
		var row MetricDiagnosisFieldStatusSummaryRow
		var statDate scanDate
		if err := rows.Scan(
			&statDate,
			&row.Protocol,
			&row.Diagnosis,
			&row.Reason,
			&row.RealtimeMileageFieldStatus,
			&row.Count,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.Severity = metricDiagnosisIssueSeverity(row.Diagnosis, row.Reason)
		row.FieldStatusSeverity = metricDiagnosisMileageFieldStatusSeverity(row.RealtimeMileageFieldStatus)
		row.RecommendedOperatorAction = metricDiagnosisRecommendedOperatorAction(row.Diagnosis, row.Reason)
		row.FieldStatusAction = metricDiagnosisMileageFieldStatusAction(row.RealtimeMileageFieldStatus)
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) QuerySourceQualitySummary(ctx context.Context, query MetricSourceQuery) ([]MetricSourceQualitySummaryRow, error) {
	query = normalizeMetricSourceQuery(query)
	sqlText, args := buildMetricSourceQualitySummarySQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricSourceQualitySummaryRow, 0)
	for rows.Next() {
		var row MetricSourceQualitySummaryRow
		var statDate scanDate
		var qualityReason sql.NullString
		if err := rows.Scan(
			&statDate,
			&row.Protocol,
			&row.QualityStatus,
			&qualityReason,
			&row.SourceCount,
			&row.VehicleCount,
			&row.SelectedCount,
			&row.SampleCount,
			&row.DailyMileageKM,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.QualityReason = qualityReason.String
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) QuerySourceSelectionSummary(ctx context.Context, query MetricSourceQuery) ([]MetricSourceSelectionSummaryRow, error) {
	query = normalizeMetricSourceQuery(query)
	sqlText, args := buildMetricSourceSelectionSummarySQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	out := make([]MetricSourceSelectionSummaryRow, 0)
	for rows.Next() {
		var row MetricSourceSelectionSummaryRow
		var statDate scanDate
		if err := rows.Scan(
			&statDate,
			&row.Protocol,
			&row.SelectionStatus,
			&row.SelectionReason,
			&row.SourceCount,
			&row.VehicleCount,
			&row.SelectedCount,
			&row.SampleCount,
			&row.DailyMileageKM,
		); err != nil {
			return nil, err
		}
		row.StatDate = statDate.String
		row.SelectionAction = metricSourceSelectionAction(row.SelectionStatus, row.SelectionReason)
		out = append(out, row)
	}
	return out, rows.Err()
}

func (r *MetricRepository) Count(ctx context.Context, query MetricQuery) (int64, error) {
	query = normalizeMetricQuery(query)
	sqlText, args := buildMetricCountSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return 0, err
	}
	defer rows.Close()

	var total int64
	if rows.Next() {
		if err := rows.Scan(&total); err != nil {
			return 0, err
		}
	}
	return total, rows.Err()
}

func (r *MetricRepository) CountSources(ctx context.Context, query MetricSourceQuery) (int64, error) {
	query = normalizeMetricSourceQuery(query)
	sqlText, args := buildMetricSourceCountSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return 0, err
	}
	defer rows.Close()

	var total int64
	if rows.Next() {
		if err := rows.Scan(&total); err != nil {
			return 0, err
		}
	}
	return total, rows.Err()
}

func (r *MetricRepository) CountSourceQualitySummary(ctx context.Context, query MetricSourceQuery) (int64, error) {
	query = normalizeMetricSourceQuery(query)
	sqlText, args := buildMetricSourceQualitySummaryCountSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return 0, err
	}
	defer rows.Close()

	var total int64
	if rows.Next() {
		if err := rows.Scan(&total); err != nil {
			return 0, err
		}
	}
	return total, rows.Err()
}

func (r *MetricRepository) CountSourceSelectionSummary(ctx context.Context, query MetricSourceQuery) (int64, error) {
	query = normalizeMetricSourceQuery(query)
	sqlText, args := buildMetricSourceSelectionSummaryCountSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return 0, err
	}
	defer rows.Close()

	var total int64
	if rows.Next() {
		if err := rows.Scan(&total); err != nil {
			return 0, err
		}
	}
	return total, rows.Err()
}

func (r *MetricRepository) CountDiagnostics(ctx context.Context, query MetricDiagnosisQuery) (int64, error) {
	query = normalizeMetricDiagnosisQuery(query)
	sqlText, args := buildMetricDiagnosisCountSQL(query)
	rows, err := r.db.QueryContext(ctx, sqlText, args...)
	if err != nil {
		return 0, err
	}
	defer rows.Close()

	var total int64
	if rows.Next() {
		if err := rows.Scan(&total); err != nil {
			return 0, err
		}
	}
	return total, rows.Err()
}

func metricDiagnosisIssueSeverity(diagnosis, reason string) string {
	switch strings.ToUpper(strings.TrimSpace(diagnosis)) {
	case "", "OK":
		return metricDiagnosisSeverityOK
	case "MISSING_DAILY":
		switch strings.ToLower(strings.TrimSpace(reason)) {
		case "source_samples_all_invalid", "source_samples_all_excluded":
			return metricDiagnosisSeveritySourceData
		}
		return metricDiagnosisSeverityPipeline
	case "NO_SOURCE_SAMPLE":
		return metricDiagnosisSeverityPipeline
	case "NO_TOTAL_MILEAGE":
		switch strings.ToLower(strings.TrimSpace(reason)) {
		case "realtime_total_mileage_time_missing":
			return metricDiagnosisSeverityPipeline
		default:
			return metricDiagnosisSeveritySourceData
		}
	default:
		return metricDiagnosisSeverityPipeline
	}
}

func metricDiagnosisRecommendedOperatorAction(diagnosis, reason string) string {
	diagnosis = strings.ToUpper(strings.TrimSpace(diagnosis))
	reason = strings.ToLower(strings.TrimSpace(reason))
	switch diagnosis {
	case "", "OK":
		return "无需处理，车辆当日里程已生成"
	case "MISSING_DAILY":
		switch reason {
		case "source_samples_all_invalid":
			return "统计候选样本已生成，但全部被质量规则排除，优先查看 source quality/selection 的 INVALID_DELTA、里程回退或异常跳变原因"
		case "source_samples_all_excluded":
			return "统计候选样本质量可用，但没有可参与选举的来源，优先检查 vehicle_data_source 是否被禁用或来源配置是否缺失"
		}
		return "统计源样本已存在但最终日表缺失，优先排查 stat-writer 投影任务、MySQL 写入和 source 选举"
	case "NO_SOURCE_SAMPLE":
		return "实时位置已有有效总里程但统计字段样本缺失，优先排查字段流 topic、stat-writer 消费和协议字段映射"
	case "NO_TOTAL_MILEAGE":
		switch reason {
		case "realtime_total_mileage_time_missing":
			return "总里程值存在但事件时间缺失，优先排查解析器和 history/realtime 写入的时间字段"
		case "realtime_total_mileage_missing":
			return "当日实时数据缺少总里程字段，核对平台是否上报该字段以及协议字段映射是否覆盖"
		case "realtime_total_mileage_non_positive":
			return "终端上报总里程小于等于 0，不参与差值统计，需核对设备或平台里程字段来源"
		case "realtime_total_mileage_not_reported_on_stat_date":
			return "最新总里程不是统计日期内上报；snapshot 里的里程可能是上一条完整帧保留值，需核对同日 raw_frames 是否真实上报总里程"
		default:
			return "车辆当日在线但没有可用于里程统计的总里程，优先核对源平台报文字段和解析映射"
		}
	default:
		return "诊断状态未知，检查统计诊断 SQL 和上游字段流"
	}
}

func metricDiagnosisMileageFieldStatusSeverity(status string) string {
	switch strings.ToLower(strings.TrimSpace(status)) {
	case "daily_metric_exists":
		return metricDiagnosisSeverityOK
	case "source_sample_exists", "standard_field_not_consumed", "standard_field_time_missing", "candidate_field_unmapped", "no_realtime_fields":
		return metricDiagnosisSeverityPipeline
	case "standard_field_non_positive", "standard_field_stale", "mapped_protocol_field_without_fresh_evidence", "no_candidate_mileage_field":
		return metricDiagnosisSeveritySourceData
	default:
		return metricDiagnosisSeverityPipeline
	}
}

func metricDiagnosisMileageFieldStatusAction(status string) string {
	switch strings.ToLower(strings.TrimSpace(status)) {
	case "daily_metric_exists":
		return "最终日里程已生成，无需处理"
	case "source_sample_exists":
		return "统计候选样本已存在但最终表缺失，优先排查 source 选举和日表 upsert"
	case "standard_field_not_consumed":
		return "实时位置已有当日有效总里程但 stat-writer 未形成候选样本，优先排查字段流 topic 和消费位点"
	case "standard_field_non_positive":
		return "源头总里程小于等于 0，当前不参与差值统计，需要核对终端或平台字段来源"
	case "standard_field_time_missing":
		return "总里程值存在但缺少事件时间，优先修复协议解析或实时写入的时间字段"
	case "standard_field_stale":
		return "标准总里程仍停留在非统计日期；snapshot 字段可能是合并保留值，以同日 raw_frames 是否包含总里程为准"
	case "candidate_field_unmapped":
		return "实时字段中存在疑似里程字段但未进入标准 total_mileage_km，优先补字段映射和单位换算"
	case "mapped_protocol_field_without_fresh_evidence":
		return "合并快照保留了协议里程字段，但没有对应的新鲜里程时间；不能据此判定当前帧已上报，请按 raw_frame_query_path 核对统计日原始帧"
	case "no_candidate_mileage_field":
		return "实时字段中没有疑似里程字段，优先向源平台核对是否上报总里程"
	case "no_realtime_fields":
		return "实时快照没有解析字段，优先排查协议解析、实时写入和 snapshot 表更新"
	default:
		return "字段状态未知，检查诊断 SQL 和实时快照数据"
	}
}

func metricSourceSelectionEvidence(row MetricSourceRow) (status string, reason string, action string) {
	if row.IsSelected {
		status, reason = "selected", "selected_current_projection"
		return status, reason, metricSourceSelectionAction(status, reason)
	}
	qualityStatus := strings.ToUpper(strings.TrimSpace(row.QualityStatus))
	qualityReason := strings.ToLower(strings.TrimSpace(row.QualityReason))
	if qualityStatus != "" && qualityStatus != QualityOK {
		if qualityReason == "" {
			qualityReason = strings.ToLower(qualityStatus)
		}
		status, reason = "excluded", "quality_"+qualityReason
		return status, reason, metricSourceSelectionAction(status, reason)
	}
	if row.SourceEnabled != nil && !*row.SourceEnabled {
		status, reason = "excluded", "source_disabled"
		return status, reason, metricSourceSelectionAction(status, reason)
	}
	if row.SourceID == nil {
		if normalizeSourceKindForRead(row.SourceKind) == "DIRECT" {
			status, reason = "not_selected", "lower_trust_priority_or_sample_count"
			return status, reason, metricSourceSelectionAction(status, reason)
		}
		status, reason = "not_selected", "unmanaged_source_lower_priority"
		return status, reason, metricSourceSelectionAction(status, reason)
	}
	switch normalizeSourceKindForRead(row.SourceKind) {
	case "UNKNOWN":
		status, reason = "not_selected", "unknown_source_kind_lower_priority"
	default:
		status, reason = "not_selected", "lower_trust_priority_or_sample_count"
	}
	return status, reason, metricSourceSelectionAction(status, reason)
}

func metricSourceSelectionAction(status string, reason string) string {
	switch strings.ToLower(strings.TrimSpace(reason)) {
	case "selected_current_projection":
		return "当前来源已被投影到最终日里程表"
	case "source_disabled":
		return "来源已在 vehicle_data_source 中禁用，不参与最终日里程选举"
	case "unmanaged_source_lower_priority":
		return "来源尚未纳入 vehicle_data_source 管理；质量可用但未被本次选中"
	case "unknown_source_kind_lower_priority":
		return "来源类型仍是 UNKNOWN；质量可用但优先级低于已配置来源"
	case "lower_trust_priority_or_sample_count":
		return "来源质量可用，但被更高可信优先级、更多样本或更新时间更新的来源覆盖"
	default:
		if strings.EqualFold(strings.TrimSpace(status), "excluded") && strings.HasPrefix(strings.ToLower(strings.TrimSpace(reason)), "quality_") {
			return "来源质量未通过，不参与最终日里程选举"
		}
		return "来源选举状态未知，检查 vehicle_daily_mileage_source 与 vehicle_data_source"
	}
}

func metricDiagnosisRealtimeFields(raw string, limit int) (int, []string) {
	keys := metricDiagnosisRealtimeFieldKeys(raw)
	if limit <= 0 || len(keys) <= limit {
		return len(keys), keys
	}
	return len(keys), keys[:limit]
}

func metricDiagnosisRealtimeFieldKeys(raw string) []string {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return nil
	}
	var values map[string]any
	if err := json.Unmarshal([]byte(raw), &values); err != nil || len(values) == 0 {
		return nil
	}
	keys := make([]string, 0, len(values))
	for key := range values {
		key = strings.TrimSpace(key)
		if key != "" {
			keys = append(keys, key)
		}
	}
	sort.Strings(keys)
	return keys
}

func metricDiagnosisRealtimeMileageEvidence(protocol string, diagnosis string, reason string, fieldCount int, sampleFields []string, raw string, realtimeTotal *float64, realtimeTotalAt string, statDate string) (string, []string, string) {
	if strings.ToUpper(strings.TrimSpace(diagnosis)) != "NO_TOTAL_MILEAGE" {
		return "", nil, ""
	}
	reason = strings.ToLower(strings.TrimSpace(reason))
	if reason != "realtime_total_mileage_missing" && reason != "realtime_total_mileage_not_reported_on_stat_date" {
		return "", nil, ""
	}
	if realtimeTotal != nil {
		switch {
		case *realtimeTotal <= 0:
			return "standard_field_non_positive", nil, "实时位置标准总里程小于等于 0，当前不参与差值统计"
		case strings.TrimSpace(realtimeTotalAt) == "":
			return "standard_field_time_missing", nil, "实时位置标准总里程已存在，但缺少该字段对应的事件时间"
		case reason == "realtime_total_mileage_not_reported_on_stat_date" || !dateTimeInStatDate(realtimeTotalAt, statDate):
			return "standard_field_stale", nil, "实时位置标准总里程已存在，但该里程时间不在统计日内；snapshot 可能保留上一条完整帧字段，请用 raw_frame_query_path 核对同日原始帧是否真实上报总里程"
		}
	}
	if fieldCount == 0 {
		return "no_realtime_fields", nil, "实时快照没有解析字段，优先检查协议解析、实时写入和 snapshot 表更新"
	}
	keys := metricDiagnosisRealtimeFieldKeys(raw)
	if len(keys) == 0 {
		keys = sampleFields
	}
	candidates := metricDiagnosisMileageCandidateFields(keys, metricDiagnosisMileageCandidateLimit)
	if len(candidates) == 0 {
		return "no_candidate_mileage_field", nil, "实时快照已有字段，但没有发现 mileage/odometer/odo 等疑似总里程字段；大概率源头未上报可用总里程，必要时查询 raw_frames 核对是否上报了 0 或异常值"
	}
	if known := knownMappedMileageFields(protocol, candidates); len(known) > 0 {
		return "mapped_protocol_field_without_fresh_evidence", known, "合并快照保留了已知协议总里程字段，但没有对应的新鲜 total_mileage_event_time；该字段可能来自历史完整帧，不能作为当前帧上报证据"
	}
	return "candidate_field_unmapped", candidates, "实时快照存在疑似里程字段但未进入标准 total_mileage_km，优先检查协议字段映射、单位换算和实时 location 写入"
}

func dateTimeInStatDate(value string, statDate string) bool {
	value = strings.TrimSpace(value)
	statDate = strings.TrimSpace(statDate)
	if value == "" || statDate == "" {
		return false
	}
	return strings.HasPrefix(value, statDate+" ")
}

func knownMappedMileageFields(protocol string, candidates []string) []string {
	if len(candidates) == 0 {
		return nil
	}
	known := map[string]struct{}{}
	for _, mapping := range mileageMappingsByProtocol(envelope.Protocol(strings.TrimSpace(protocol))) {
		known[strings.ToLower(strings.TrimSpace(mapping.Key))] = struct{}{}
	}
	out := make([]string, 0, len(candidates))
	for _, candidate := range candidates {
		normalized := strings.ToLower(strings.TrimSpace(candidate))
		if _, ok := known[normalized]; !ok {
			continue
		}
		out = append(out, candidate)
	}
	return out
}

func metricDiagnosisRawFrameQueryPath(row MetricDiagnosisRow) string {
	if strings.TrimSpace(row.VIN) == "" || strings.TrimSpace(row.Protocol) == "" || strings.TrimSpace(row.StatDate) == "" {
		return ""
	}
	dateFrom, dateTo := metricDiagnosisDateWindow(row.StatDate)
	values := url.Values{}
	values.Set("protocol", row.Protocol)
	values.Set("vin", row.VIN)
	values.Set("dateFrom", dateFrom)
	values.Set("dateTo", dateTo)
	values.Set("orderBy", "eventTime")
	values.Set("includeFields", "true")
	values.Set("limit", "20")
	return "/api/history/raw-frames?" + values.Encode()
}

func metricDiagnosisSourceSampleQueryPath(row MetricDiagnosisRow) string {
	if strings.TrimSpace(row.VIN) == "" || strings.TrimSpace(row.Protocol) == "" || strings.TrimSpace(row.StatDate) == "" {
		return ""
	}
	values := url.Values{}
	values.Set("vin", row.VIN)
	values.Set("protocol", row.Protocol)
	values.Set("dateFrom", row.StatDate)
	values.Set("dateTo", row.StatDate)
	values.Set("includeTotal", "true")
	values.Set("limit", "50")
	return "/api/stats/daily-metrics/sources?" + values.Encode()
}

func metricDiagnosisDateWindow(statDate string) (string, string) {
	date := strings.TrimSpace(statDate)
	start, err := time.Parse("2006-01-02", date)
	if err != nil {
		return date, date
	}
	return start.Format("2006-01-02 15:04:05"), start.AddDate(0, 0, 1).Format("2006-01-02 15:04:05")
}

func metricDiagnosisMileageCandidateFields(fields []string, limit int) []string {
	out := make([]string, 0)
	seen := map[string]struct{}{}
	for _, field := range fields {
		field = strings.TrimSpace(field)
		if field == "" || !looksLikeMileageField(field) {
			continue
		}
		if _, ok := seen[field]; ok {
			continue
		}
		seen[field] = struct{}{}
		out = append(out, field)
		if limit > 0 && len(out) >= limit {
			return out
		}
	}
	return out
}

func looksLikeMileageField(field string) bool {
	normalized := strings.ToLower(strings.TrimSpace(field))
	normalized = strings.ReplaceAll(normalized, "-", "_")
	normalized = strings.ReplaceAll(normalized, "/", ".")
	switch {
	case strings.Contains(normalized, "mileage"):
		return true
	case strings.Contains(normalized, "odometer"):
		return true
	case strings.Contains(normalized, ".odo"):
		return true
	case strings.Contains(normalized, "_odo"):
		return true
	case strings.HasSuffix(normalized, ".odo"):
		return true
	case strings.HasSuffix(normalized, "_odo"):
		return true
	default:
		return false
	}
}

func metricDiagnosisReasonSummaryIssueTotals(rows []MetricDiagnosisReasonSummaryRow) (vehicleTotal, actionableTotal, pipelineTotal, sourceDataTotal int64) {
	for _, row := range rows {
		vehicleTotal += row.Count
		switch row.Severity {
		case metricDiagnosisSeverityPipeline:
			actionableTotal += row.Count
			pipelineTotal += row.Count
		case metricDiagnosisSeveritySourceData:
			actionableTotal += row.Count
			sourceDataTotal += row.Count
		default:
			if strings.ToUpper(strings.TrimSpace(row.Diagnosis)) != "OK" {
				actionableTotal += row.Count
			}
		}
	}
	return vehicleTotal, actionableTotal, pipelineTotal, sourceDataTotal
}

func metricDiagnosisFieldStatusSummaryIssueTotals(rows []MetricDiagnosisFieldStatusSummaryRow) (vehicleTotal, actionableTotal, pipelineTotal, sourceDataTotal int64) {
	for _, row := range rows {
		vehicleTotal += row.Count
		severity := strings.ToLower(strings.TrimSpace(row.Severity))
		if severity == "" {
			severity = strings.ToLower(strings.TrimSpace(row.FieldStatusSeverity))
		}
		switch severity {
		case metricDiagnosisSeverityPipeline:
			actionableTotal += row.Count
			pipelineTotal += row.Count
		case metricDiagnosisSeveritySourceData:
			actionableTotal += row.Count
			sourceDataTotal += row.Count
		default:
			if strings.ToUpper(strings.TrimSpace(row.Diagnosis)) != "OK" {
				actionableTotal += row.Count
			}
		}
	}
	return vehicleTotal, actionableTotal, pipelineTotal, sourceDataTotal
}

func normalizeMetricQuery(query MetricQuery) MetricQuery {
	query.VIN = strings.TrimSpace(query.VIN)
	query.Protocol = strings.ToUpper(strings.TrimSpace(query.Protocol))
	query.DateFrom = strings.TrimSpace(query.DateFrom)
	query.DateTo = strings.TrimSpace(query.DateTo)
	if query.Limit <= 0 {
		query.Limit = 50
	}
	return query
}

func normalizeMetricSourceQuery(query MetricSourceQuery) MetricSourceQuery {
	query.VIN = strings.TrimSpace(query.VIN)
	query.Protocol = strings.ToUpper(strings.TrimSpace(query.Protocol))
	query.DateFrom = strings.TrimSpace(query.DateFrom)
	query.DateTo = strings.TrimSpace(query.DateTo)
	query.SourceIP = strings.TrimSpace(query.SourceIP)
	query.QualityStatus = strings.ToUpper(strings.TrimSpace(query.QualityStatus))
	query.QualityReason = strings.ToLower(strings.TrimSpace(query.QualityReason))
	query.OrderBy = normalizeMetricSourceOrderBy(query.OrderBy)
	if query.Limit <= 0 {
		query.Limit = 50
	}
	return query
}

func normalizeMetricSourceOrderBy(value string) string {
	normalized := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(value), "_", ""))
	switch normalized {
	case "dailymileagedesc", "dailymileage":
		return "dailyMileageDesc"
	default:
		return ""
	}
}

func normalizeMetricDiagnosisQuery(query MetricDiagnosisQuery) MetricDiagnosisQuery {
	query.VIN = strings.TrimSpace(query.VIN)
	query.Protocol = strings.ToUpper(strings.TrimSpace(query.Protocol))
	query.Date = strings.TrimSpace(query.Date)
	if query.Date == "" {
		query.Date = time.Now().In(time.FixedZone("Asia/Shanghai", 8*3600)).Format("2006-01-02")
	}
	query.Diagnosis = strings.ToUpper(strings.TrimSpace(query.Diagnosis))
	query.Reason = strings.ToLower(strings.TrimSpace(query.Reason))
	query.Severity = strings.ToLower(strings.TrimSpace(query.Severity))
	if query.Limit <= 0 {
		query.Limit = 50
	}
	return query
}

func buildMetricSQL(query MetricQuery) (string, []any) {
	where, args := buildMetricWhere(query)
	sqlText := `SELECT m.vin, m.stat_date, m.protocol, m.source_id,
       ds.source_ip, ds.latest_source_endpoint, ds.platform_name, ds.source_code, ds.source_kind,
       m.daily_mileage_km, m.latest_total_mileage_km, m.updated_at
FROM vehicle_daily_mileage m
LEFT JOIN vehicle_data_source ds ON ds.id = m.source_id`
	if len(where) > 0 {
		sqlText += " WHERE " + strings.Join(where, " AND ")
	}
	sqlText += " ORDER BY m.stat_date DESC, m.vin ASC, m.protocol ASC LIMIT ? OFFSET ?"
	args = append(args, query.Limit, query.Offset)
	return sqlText, args
}

func buildMetricSourceSQL(query MetricSourceQuery) (string, []any) {
	where, args := buildMetricSourceWhere(query)
	sqlText := `SELECT s.vin, s.stat_date, s.protocol, s.source_key, s.source_ip, s.source_endpoint, s.phone, s.device_id,
       COALESCE(NULLIF(TRIM(s.platform_name), ''), ds.platform_name) AS platform_name,
       ds.id, ds.source_code, ` + metricSourceKindSQL("s") + ` AS source_kind, ds.enabled, ds.trust_priority,
       s.first_total_mileage_km, s.latest_total_mileage_km, s.daily_mileage_km, s.sample_count,
       s.first_event_time, s.latest_event_time, s.quality_status, s.quality_reason, s.is_selected, s.updated_at
FROM vehicle_daily_mileage_source s
LEFT JOIN vehicle_data_source ds ON ds.protocol = s.protocol AND ds.source_ip = s.source_ip`
	if len(where) > 0 {
		sqlText += " WHERE " + strings.Join(where, " AND ")
	}
	sqlText += metricSourceOrderBySQL(query)
	args = append(args, query.Limit, query.Offset)
	return sqlText, args
}

func metricSourceOrderBySQL(query MetricSourceQuery) string {
	if query.OrderBy == "dailyMileageDesc" {
		return ` ORDER BY s.daily_mileage_km DESC, s.sample_count DESC, s.latest_event_time DESC,
         s.stat_date DESC, s.vin ASC, s.protocol ASC, s.is_selected DESC,
         CASE ` + metricSourceKindSQL("s") + `
           WHEN 'PLATFORM' THEN 0
           WHEN 'DIRECT' THEN 1
           WHEN 'UNKNOWN' THEN 2
           ELSE 3
         END,
         ds.trust_priority,
         s.source_key ASC
LIMIT ? OFFSET ?`
	}
	return ` ORDER BY s.stat_date DESC, s.vin ASC, s.protocol ASC, s.is_selected DESC,
         CASE ` + metricSourceKindSQL("s") + `
           WHEN 'PLATFORM' THEN 0
           WHEN 'DIRECT' THEN 1
           WHEN 'UNKNOWN' THEN 2
           ELSE 3
         END,
         ds.trust_priority,
         s.sample_count DESC,
         s.latest_event_time DESC,
         s.source_key ASC
LIMIT ? OFFSET ?`
}

func buildMetricSourceQualitySummarySQL(query MetricSourceQuery) (string, []any) {
	where, args := buildMetricSourceWhere(query)
	sqlText := `SELECT s.stat_date, s.protocol, s.quality_status, s.quality_reason,
       COUNT(*) AS source_count,
       COUNT(DISTINCT s.vin) AS vehicle_count,
       SUM(CASE WHEN s.is_selected = 1 THEN 1 ELSE 0 END) AS selected_count,
       COALESCE(SUM(s.sample_count), 0) AS sample_count,
       COALESCE(SUM(s.daily_mileage_km), 0) AS daily_mileage_km
FROM vehicle_daily_mileage_source s`
	if len(where) > 0 {
		sqlText += " WHERE " + strings.Join(where, " AND ")
	}
	sqlText += ` GROUP BY s.stat_date, s.protocol, s.quality_status, s.quality_reason
ORDER BY s.stat_date DESC,
         s.protocol ASC,
         CASE s.quality_status
           WHEN 'OK' THEN 0
           WHEN 'NO_PREVIOUS_BASELINE' THEN 1
           WHEN 'INVALID_DELTA' THEN 2
           ELSE 3
         END,
         source_count DESC,
         s.quality_reason ASC
LIMIT ? OFFSET ?`
	args = append(args, query.Limit, query.Offset)
	return sqlText, args
}

func buildMetricSourceSelectionSummarySQL(query MetricSourceQuery) (string, []any) {
	baseSQL, args := buildMetricSourceSelectionSummaryBaseSQL(query)
	sqlText := `SELECT stat_date, protocol, selection_status, selection_reason,
       COUNT(*) AS source_count,
       COUNT(DISTINCT vin) AS vehicle_count,
       SUM(CASE WHEN is_selected = 1 THEN 1 ELSE 0 END) AS selected_count,
       COALESCE(SUM(sample_count), 0) AS sample_count,
       COALESCE(SUM(daily_mileage_km), 0) AS daily_mileage_km
FROM (` + baseSQL + `
) source_selection
GROUP BY stat_date, protocol, selection_status, selection_reason
ORDER BY stat_date DESC,
         protocol ASC,
         CASE selection_status
           WHEN 'selected' THEN 0
           WHEN 'excluded' THEN 1
           WHEN 'not_selected' THEN 2
           ELSE 3
         END,
         source_count DESC,
         selection_reason ASC
LIMIT ? OFFSET ?`
	args = append(args, query.Limit, query.Offset)
	return sqlText, args
}

func buildMetricDiagnosisSQL(query MetricDiagnosisQuery) (string, []any) {
	fromSQL, args := buildMetricDiagnosisFromSQL(query)
	sqlText := `SELECT rt.vin, ? AS stat_date, rt.protocol,
       COALESCE(NULLIF(s.plate, ''), NULLIF(l.plate, ''), '') AS plate,
       COALESCE(NULLIF(s.platform_name, ''), '') AS platform_name,
       COALESCE(NULLIF(s.peer, ''), '') AS peer,
       s.event_time AS snapshot_event_time,
       l.event_time AS location_event_time,
       s.updated_at AS snapshot_updated_at,
       l.updated_at AS location_updated_at,
       l.total_mileage_km AS realtime_total_mileage_km,
       l.total_mileage_event_time AS realtime_total_mileage_event_time,
       m.daily_mileage_km,
	       m.latest_total_mileage_km AS daily_latest_total_mileage_km,
	       COALESCE(ms.sample_count, 0) AS source_sample_count,
	       COALESCE(ms.ok_source_count, 0) AS ok_source_count,
       COALESCE(ms.selectable_source_count, 0) AS selectable_source_count,
	       ms.latest_event_time AS latest_stat_event_time,
       COALESCE(ms.quality_statuses, '') AS quality_statuses,
       s.parsed_json AS snapshot_parsed_json
` + fromSQL + `
ORDER BY CASE WHEN m.vin IS NULL THEN 0 ELSE 1 END, rt.protocol ASC, rt.vin ASC
LIMIT ? OFFSET ?`
	args = append([]any{query.Date}, args...)
	args = append(args, query.Limit, query.Offset)
	return sqlText, args
}

func buildMetricDiagnosisCountSQL(query MetricDiagnosisQuery) (string, []any) {
	fromSQL, args := buildMetricDiagnosisFromSQL(query)
	return "SELECT COUNT(*) " + fromSQL, args
}

func buildMetricDiagnosisSummarySQL(query MetricDiagnosisQuery) (string, []any) {
	query.Diagnosis = ""
	fromSQL, fromArgs := buildMetricDiagnosisFromSQL(query)
	sqlText := `SELECT ? AS stat_date,
       rt.protocol,
       COUNT(*) AS active_count,
       SUM(CASE WHEN m.vin IS NOT NULL THEN 1 ELSE 0 END) AS ok_count,
       SUM(CASE WHEN m.vin IS NULL AND COALESCE(ms.sample_count, 0) > 0 THEN 1 ELSE 0 END) AS missing_daily_count,
       SUM(CASE WHEN m.vin IS NULL AND COALESCE(ms.sample_count, 0) = 0
                  AND l.total_mileage_km > 0
                  AND l.total_mileage_event_time >= ? AND l.total_mileage_event_time < DATE_ADD(?, INTERVAL 1 DAY)
                THEN 1 ELSE 0 END) AS no_source_sample_count,
       SUM(CASE WHEN m.vin IS NULL AND COALESCE(ms.sample_count, 0) = 0
                  AND (l.total_mileage_km IS NULL OR l.total_mileage_km <= 0 OR l.total_mileage_event_time IS NULL OR l.total_mileage_event_time < ? OR l.total_mileage_event_time >= DATE_ADD(?, INTERVAL 1 DAY))
                THEN 1 ELSE 0 END) AS no_total_mileage_count
` + fromSQL + `
GROUP BY rt.protocol
ORDER BY rt.protocol ASC`
	args := []any{query.Date, query.Date, query.Date, query.Date, query.Date}
	args = append(args, fromArgs...)
	return sqlText, args
}

func buildMetricDiagnosisReasonSummarySQL(query MetricDiagnosisQuery) (string, []any) {
	baseQuery := query
	baseQuery.Diagnosis = ""
	baseQuery.Reason = ""
	baseQuery.Severity = ""
	fromSQL, fromArgs := buildMetricDiagnosisFromSQL(baseQuery)
	diagnosisSQL, diagnosisArgs := metricDiagnosisCaseSQL(query.Date)
	reasonSQL, reasonArgs := metricDiagnosisReasonCaseSQL(query.Date)
	severitySQL, severityArgs := metricDiagnosisSeverityCaseSQL(query.Date)
	sqlText := `SELECT stat_date, protocol, diagnosis, reason, COUNT(*) AS count
FROM (
  SELECT ? AS stat_date,
         rt.protocol,
         ` + diagnosisSQL + ` AS diagnosis,
         ` + reasonSQL + ` AS reason,
         ` + severitySQL + ` AS severity
` + fromSQL + `
) diagnostic_rows`
	args := []any{query.Date}
	args = append(args, diagnosisArgs...)
	args = append(args, reasonArgs...)
	args = append(args, severityArgs...)
	args = append(args, fromArgs...)
	var outerWhere []string
	if query.Diagnosis != "" {
		outerWhere = append(outerWhere, "diagnosis = ?")
		args = append(args, query.Diagnosis)
	}
	if query.Reason != "" {
		outerWhere = append(outerWhere, "reason = ?")
		args = append(args, query.Reason)
	}
	if query.Severity != "" {
		outerWhere = append(outerWhere, "severity = ?")
		args = append(args, query.Severity)
	}
	if len(outerWhere) > 0 {
		sqlText += " WHERE " + strings.Join(outerWhere, " AND ")
	}
	sqlText += `
GROUP BY stat_date, protocol, diagnosis, reason
ORDER BY protocol ASC,
         CASE diagnosis
           WHEN 'OK' THEN 0
           WHEN 'MISSING_DAILY' THEN 1
           WHEN 'NO_SOURCE_SAMPLE' THEN 2
           WHEN 'NO_TOTAL_MILEAGE' THEN 3
           ELSE 4
         END,
         reason ASC`
	return sqlText, args
}

func buildMetricDiagnosisFieldStatusSummarySQL(query MetricDiagnosisQuery) (string, []any) {
	baseQuery := query
	baseQuery.Diagnosis = ""
	baseQuery.Reason = ""
	baseQuery.Severity = ""
	fromSQL, fromArgs := buildMetricDiagnosisFromSQL(baseQuery)
	diagnosisSQL, diagnosisArgs := metricDiagnosisCaseSQL(query.Date)
	reasonSQL, reasonArgs := metricDiagnosisReasonCaseSQL(query.Date)
	severitySQL, severityArgs := metricDiagnosisSeverityCaseSQL(query.Date)
	fieldStatusSQL, fieldStatusArgs := metricDiagnosisMileageFieldStatusCaseSQL(query.Date)
	sqlText := `SELECT stat_date, protocol, diagnosis, reason, realtime_mileage_field_status, COUNT(*) AS count
FROM (
  SELECT ? AS stat_date,
         rt.protocol,
         ` + diagnosisSQL + ` AS diagnosis,
         ` + reasonSQL + ` AS reason,
         ` + severitySQL + ` AS severity,
         ` + fieldStatusSQL + ` AS realtime_mileage_field_status
` + fromSQL + `
) diagnostic_rows`
	args := []any{query.Date}
	args = append(args, diagnosisArgs...)
	args = append(args, reasonArgs...)
	args = append(args, severityArgs...)
	args = append(args, fieldStatusArgs...)
	args = append(args, fromArgs...)
	var outerWhere []string
	if query.Diagnosis != "" {
		outerWhere = append(outerWhere, "diagnosis = ?")
		args = append(args, query.Diagnosis)
	}
	if query.Reason != "" {
		outerWhere = append(outerWhere, "reason = ?")
		args = append(args, query.Reason)
	}
	if query.Severity != "" {
		outerWhere = append(outerWhere, "severity = ?")
		args = append(args, query.Severity)
	}
	if len(outerWhere) > 0 {
		sqlText += " WHERE " + strings.Join(outerWhere, " AND ")
	}
	sqlText += `
GROUP BY stat_date, protocol, diagnosis, reason, realtime_mileage_field_status
ORDER BY protocol ASC,
         CASE diagnosis
           WHEN 'OK' THEN 0
           WHEN 'MISSING_DAILY' THEN 1
           WHEN 'NO_SOURCE_SAMPLE' THEN 2
           WHEN 'NO_TOTAL_MILEAGE' THEN 3
           ELSE 4
         END,
         reason ASC,
         count DESC,
         realtime_mileage_field_status ASC`
	return sqlText, args
}

func metricDiagnosisCaseSQL(statDate string) (string, []any) {
	return `CASE
           WHEN m.vin IS NOT NULL THEN 'OK'
           WHEN COALESCE(ms.sample_count, 0) > 0 THEN 'MISSING_DAILY'
           WHEN l.total_mileage_km > 0
             AND l.total_mileage_event_time >= ? AND l.total_mileage_event_time < DATE_ADD(?, INTERVAL 1 DAY)
             THEN 'NO_SOURCE_SAMPLE'
           ELSE 'NO_TOTAL_MILEAGE'
         END`, []any{statDate, statDate}
}

func metricDiagnosisReasonCaseSQL(statDate string) (string, []any) {
	return `CASE
           WHEN m.vin IS NOT NULL THEN 'daily_metric_exists'
           WHEN COALESCE(ms.sample_count, 0) > 0 AND COALESCE(ms.ok_source_count, 0) = 0 THEN 'source_samples_all_invalid'
           WHEN COALESCE(ms.sample_count, 0) > 0 AND COALESCE(ms.selectable_source_count, 0) = 0 THEN 'source_samples_all_excluded'
           WHEN COALESCE(ms.sample_count, 0) > 0 THEN 'source_samples_exist_but_final_metric_missing'
           WHEN l.total_mileage_km > 0
             AND l.total_mileage_event_time >= ? AND l.total_mileage_event_time < DATE_ADD(?, INTERVAL 1 DAY)
             THEN 'realtime_location_has_total_mileage_but_no_stat_sample'
           WHEN l.total_mileage_km IS NULL THEN 'realtime_total_mileage_missing'
           WHEN l.total_mileage_km <= 0 THEN 'realtime_total_mileage_non_positive'
           WHEN l.total_mileage_event_time IS NULL THEN 'realtime_total_mileage_time_missing'
           WHEN l.total_mileage_event_time < ? OR l.total_mileage_event_time >= DATE_ADD(?, INTERVAL 1 DAY)
             THEN 'realtime_total_mileage_not_reported_on_stat_date'
           ELSE 'realtime_active_without_total_mileage'
         END`, []any{statDate, statDate, statDate, statDate}
}

func metricDiagnosisSeverityCaseSQL(statDate string) (string, []any) {
	return `CASE
           WHEN m.vin IS NOT NULL THEN 'ok'
           WHEN COALESCE(ms.sample_count, 0) > 0 AND COALESCE(ms.ok_source_count, 0) = 0 THEN 'source_data'
           WHEN COALESCE(ms.sample_count, 0) > 0 AND COALESCE(ms.selectable_source_count, 0) = 0 THEN 'source_data'
           WHEN COALESCE(ms.sample_count, 0) > 0 THEN 'pipeline'
           WHEN l.total_mileage_km > 0
             AND l.total_mileage_event_time >= ? AND l.total_mileage_event_time < DATE_ADD(?, INTERVAL 1 DAY)
             THEN 'pipeline'
           WHEN l.total_mileage_km IS NULL THEN 'source_data'
           WHEN l.total_mileage_km <= 0 THEN 'source_data'
           WHEN l.total_mileage_event_time IS NULL THEN 'pipeline'
           ELSE 'source_data'
         END`, []any{statDate, statDate}
}

func metricDiagnosisMileageFieldStatusCaseSQL(statDate string) (string, []any) {
	return `CASE
           WHEN m.vin IS NOT NULL THEN 'daily_metric_exists'
           WHEN COALESCE(ms.sample_count, 0) > 0 THEN 'source_sample_exists'
           WHEN l.total_mileage_km > 0
             AND l.total_mileage_event_time >= ? AND l.total_mileage_event_time < DATE_ADD(?, INTERVAL 1 DAY)
             THEN 'standard_field_not_consumed'
           WHEN l.total_mileage_km <= 0 THEN 'standard_field_non_positive'
           WHEN l.total_mileage_km > 0 AND l.total_mileage_event_time IS NULL THEN 'standard_field_time_missing'
           WHEN l.total_mileage_km > 0
             AND (l.total_mileage_event_time < ? OR l.total_mileage_event_time >= DATE_ADD(?, INTERVAL 1 DAY))
             THEN 'standard_field_stale'
           WHEN s.parsed_json IS NULL OR TRIM(s.parsed_json) = '' OR TRIM(s.parsed_json) = '{}' THEN 'no_realtime_fields'
           WHEN rt.protocol = 'YUTONG_MQTT'
             AND (
               LOWER(s.parsed_json) LIKE '%yutong_mqtt.data.total_mileage%'
               OR LOWER(s.parsed_json) LIKE '%yutong_mqtt.root.data.total_mileage%'
             )
	             THEN 'mapped_protocol_field_without_fresh_evidence'
           WHEN LOWER(s.parsed_json) LIKE '%mileage%'
             OR LOWER(s.parsed_json) LIKE '%odometer%'
             OR LOWER(s.parsed_json) LIKE '%.odo%'
             OR LOWER(s.parsed_json) LIKE '%_odo%'
             THEN 'candidate_field_unmapped'
           ELSE 'no_candidate_mileage_field'
         END`, []any{statDate, statDate, statDate, statDate}
}

func buildMetricCountSQL(query MetricQuery) (string, []any) {
	where, args := buildMetricWhere(query)
	sqlText := `SELECT COUNT(*) FROM vehicle_daily_mileage m`
	if len(where) > 0 {
		sqlText += " WHERE " + strings.Join(where, " AND ")
	}
	return sqlText, args
}

func buildMetricSourceCountSQL(query MetricSourceQuery) (string, []any) {
	where, args := buildMetricSourceWhere(query)
	sqlText := `SELECT COUNT(*) FROM vehicle_daily_mileage_source s`
	if len(where) > 0 {
		sqlText += " WHERE " + strings.Join(where, " AND ")
	}
	return sqlText, args
}

func buildMetricSourceQualitySummaryCountSQL(query MetricSourceQuery) (string, []any) {
	where, args := buildMetricSourceWhere(query)
	sqlText := `SELECT COUNT(*) FROM (
  SELECT 1
  FROM vehicle_daily_mileage_source s`
	if len(where) > 0 {
		sqlText += " WHERE " + strings.Join(where, " AND ")
	}
	sqlText += `
  GROUP BY s.stat_date, s.protocol, s.quality_status, s.quality_reason
) q`
	return sqlText, args
}

func buildMetricSourceSelectionSummaryCountSQL(query MetricSourceQuery) (string, []any) {
	baseSQL, args := buildMetricSourceSelectionSummaryBaseSQL(query)
	sqlText := `SELECT COUNT(*) FROM (
  SELECT 1
  FROM (` + baseSQL + `
  ) source_selection
  GROUP BY stat_date, protocol, selection_status, selection_reason
) q`
	return sqlText, args
}

func buildMetricSourceSelectionSummaryBaseSQL(query MetricSourceQuery) (string, []any) {
	where, args := buildMetricSourceWhere(query)
	sqlText := `SELECT s.stat_date, s.protocol, s.vin, s.is_selected, s.sample_count, s.daily_mileage_km,
       ` + metricSourceSelectionStatusSQL() + ` AS selection_status,
       ` + metricSourceSelectionReasonSQL() + ` AS selection_reason
FROM vehicle_daily_mileage_source s
LEFT JOIN vehicle_data_source ds ON ds.protocol = s.protocol AND ds.source_ip = s.source_ip`
	if len(where) > 0 {
		sqlText += " WHERE " + strings.Join(where, " AND ")
	}
	return sqlText, args
}

func metricSourceKindSQL(sourceAlias string) string {
	alias := strings.TrimSpace(sourceAlias)
	if alias == "" {
		alias = "s"
	}
	return `COALESCE(NULLIF(TRIM(ds.source_kind), ''), CASE WHEN ` + alias + `.source_key LIKE '%` + platformSourceKeyPrefix + `%' THEN 'PLATFORM' WHEN ` + alias + `.source_key LIKE '%` + directSourceKeySuffix + `' THEN 'DIRECT' ELSE 'UNKNOWN' END)`
}

func metricSourceSelectionStatusSQL() string {
	return `CASE
         WHEN s.is_selected = 1 THEN 'selected'
         WHEN UPPER(COALESCE(NULLIF(TRIM(s.quality_status), ''), '')) <> ''
           AND UPPER(COALESCE(NULLIF(TRIM(s.quality_status), ''), '')) <> '` + QualityOK + `' THEN 'excluded'
         WHEN ds.enabled = 0 THEN 'excluded'
         ELSE 'not_selected'
       END`
}

func metricSourceSelectionReasonSQL() string {
	return `CASE
         WHEN s.is_selected = 1 THEN 'selected_current_projection'
         WHEN UPPER(COALESCE(NULLIF(TRIM(s.quality_status), ''), '')) <> ''
           AND UPPER(COALESCE(NULLIF(TRIM(s.quality_status), ''), '')) <> '` + QualityOK + `'
           THEN CONCAT('quality_', LOWER(COALESCE(NULLIF(TRIM(s.quality_reason), ''), s.quality_status)))
         WHEN ds.enabled = 0 THEN 'source_disabled'
         WHEN ds.id IS NULL AND ` + metricSourceKindSQL("s") + ` <> 'DIRECT' THEN 'unmanaged_source_lower_priority'
         WHEN ` + metricSourceKindSQL("s") + ` = 'UNKNOWN' THEN 'unknown_source_kind_lower_priority'
         ELSE 'lower_trust_priority_or_sample_count'
       END`
}

func buildMetricDiagnosisFromSQL(query MetricDiagnosisQuery) (string, []any) {
	realtimeSQL, args := buildActiveRealtimeMetricSQL(query)
	sourceSummarySQL := `LEFT JOIN (
  SELECT s.vin, s.protocol, s.stat_date,
         SUM(s.sample_count) AS sample_count,
         SUM(CASE WHEN s.quality_status = 'OK' THEN 1 ELSE 0 END) AS ok_source_count,
         SUM(CASE
           WHEN s.quality_status = 'OK'
            AND (ds.id IS NULL OR ds.enabled = 1 OR (
              COALESCE(NULLIF(TRIM(ds.source_kind), ''), 'UNKNOWN') = 'UNKNOWN'
              AND (ds.source_code IS NULL OR TRIM(ds.source_code) = '')
              AND (ds.platform_name IS NULL OR TRIM(ds.platform_name) = '')
            ))
           THEN 1 ELSE 0
         END) AS selectable_source_count,
         MAX(s.latest_event_time) AS latest_event_time,
         GROUP_CONCAT(DISTINCT s.quality_status ORDER BY s.quality_status SEPARATOR ',') AS quality_statuses
  FROM vehicle_daily_mileage_source s
  LEFT JOIN vehicle_data_source ds ON ds.protocol = s.protocol AND ds.source_ip = s.source_ip
  WHERE s.stat_date = ?
  GROUP BY s.vin, s.protocol, s.stat_date
) ms ON ms.vin = rt.vin AND ms.protocol = rt.protocol`
	args = append(args, query.Date)
	fromSQL := `FROM (` + realtimeSQL + `) rt
LEFT JOIN vehicle_realtime_snapshot s ON s.protocol = rt.protocol AND s.vin = rt.vin
LEFT JOIN vehicle_realtime_location l ON l.protocol = rt.protocol AND l.vin = rt.vin
` + sourceSummarySQL + `
LEFT JOIN vehicle_daily_mileage m ON m.vin = rt.vin AND m.protocol = rt.protocol AND m.stat_date = ?`
	args = append(args, query.Date)
	where, whereArgs := buildMetricDiagnosisWhere(query)
	if len(where) > 0 {
		fromSQL += "\nWHERE " + strings.Join(where, " AND ")
		args = append(args, whereArgs...)
	}
	return fromSQL, args
}

func buildActiveRealtimeMetricSQL(query MetricDiagnosisQuery) (string, []any) {
	snapshotWhere, snapshotArgs := buildActiveRealtimeMetricWhere(query)
	locationWhere, locationArgs := buildActiveRealtimeMetricWhere(query)
	sqlText := `SELECT protocol, vin FROM vehicle_realtime_snapshot
  WHERE ` + snapshotWhere + `
UNION
SELECT protocol, vin FROM vehicle_realtime_location
  WHERE ` + locationWhere
	args := append(snapshotArgs, locationArgs...)
	return sqlText, args
}

func buildActiveRealtimeMetricWhere(query MetricDiagnosisQuery) (string, []any) {
	where := []string{"vin <> ''", realtimeActiveDatePredicate()}
	args := []any{
		query.Date, query.Date,
		query.Date, query.Date,
	}
	if query.Protocol != "" {
		where = append(where, "protocol = ?")
		args = append(args, query.Protocol)
	}
	if query.VIN != "" {
		where = append(where, "vin = ?")
		args = append(args, query.VIN)
	}
	return strings.Join(where, " AND "), args
}

func realtimeActiveDatePredicate() string {
	return `((event_time >= ? AND event_time < DATE_ADD(?, INTERVAL 1 DAY))
       OR (received_at >= ? AND received_at < DATE_ADD(?, INTERVAL 1 DAY)))`
}

func buildMetricWhere(query MetricQuery) ([]string, []any) {
	var where []string
	var args []any
	add := func(clause string, value any) {
		where = append(where, clause)
		args = append(args, value)
	}
	if query.VIN != "" {
		add("m.vin = ?", query.VIN)
	}
	if query.Protocol != "" {
		add("m.protocol = ?", query.Protocol)
	}
	if query.DateFrom != "" {
		add("m.stat_date >= ?", query.DateFrom)
	}
	if query.DateTo != "" {
		add("m.stat_date <= ?", query.DateTo)
	}
	return where, args
}

func buildMetricSourceWhere(query MetricSourceQuery) ([]string, []any) {
	var where []string
	var args []any
	add := func(clause string, value any) {
		where = append(where, clause)
		args = append(args, value)
	}
	if query.VIN != "" {
		add("s.vin = ?", query.VIN)
	}
	if query.Protocol != "" {
		add("s.protocol = ?", query.Protocol)
	}
	if query.DateFrom != "" {
		add("s.stat_date >= ?", query.DateFrom)
	}
	if query.DateTo != "" {
		add("s.stat_date <= ?", query.DateTo)
	}
	if query.SourceIP != "" {
		add("s.source_ip = ?", query.SourceIP)
	}
	if query.QualityStatus != "" {
		add("s.quality_status = ?", query.QualityStatus)
	}
	if query.QualityReason != "" {
		add("s.quality_reason = ?", query.QualityReason)
	}
	if query.Selected != nil {
		if *query.Selected {
			add("s.is_selected = ?", 1)
		} else {
			add("s.is_selected = ?", 0)
		}
	}
	return where, args
}

func buildMetricDiagnosisWhere(query MetricDiagnosisQuery) ([]string, []any) {
	var where []string
	var args []any
	add := func(clause string, value any) {
		where = append(where, clause)
		args = append(args, value)
	}
	if query.VIN != "" {
		add("rt.vin = ?", query.VIN)
	}
	if query.Protocol != "" {
		add("rt.protocol = ?", query.Protocol)
	}
	switch query.Diagnosis {
	case "OK":
		where = append(where, "m.vin IS NOT NULL")
	case "MISSING_DAILY":
		where = append(where, "m.vin IS NULL AND COALESCE(ms.sample_count, 0) > 0")
	case "NO_SOURCE_SAMPLE":
		where = append(where, "m.vin IS NULL AND COALESCE(ms.sample_count, 0) = 0 AND l.total_mileage_km > 0 AND l.total_mileage_event_time >= ? AND l.total_mileage_event_time < DATE_ADD(?, INTERVAL 1 DAY)")
		args = append(args, query.Date, query.Date)
	case "NO_TOTAL_MILEAGE":
		where = append(where, "m.vin IS NULL AND COALESCE(ms.sample_count, 0) = 0 AND (l.total_mileage_km IS NULL OR l.total_mileage_km <= 0 OR l.total_mileage_event_time IS NULL OR l.total_mileage_event_time < ? OR l.total_mileage_event_time >= DATE_ADD(?, INTERVAL 1 DAY))")
		args = append(args, query.Date, query.Date)
	}
	if query.Reason != "" {
		reasonSQL, reasonArgs := metricDiagnosisReasonCaseSQL(query.Date)
		where = append(where, "("+reasonSQL+") = ?")
		args = append(args, reasonArgs...)
		args = append(args, query.Reason)
	}
	if query.Severity != "" {
		severitySQL, severityArgs := metricDiagnosisSeverityCaseSQL(query.Date)
		where = append(where, "("+severitySQL+") = ?")
		args = append(args, severityArgs...)
		args = append(args, query.Severity)
	}
	return where, args
}

type MetricHandler struct {
	repository *MetricRepository
}

func NewMetricHandler(repository *MetricRepository) *MetricHandler {
	if repository == nil {
		panic("metric repository must not be nil")
	}
	return &MetricHandler{repository: repository}
}

func (h *MetricHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.Method != http.MethodGet {
		writeMetricError(w, http.StatusMethodNotAllowed, "method not allowed")
		return
	}
	switch strings.Trim(r.URL.Path, "/") {
	case "api/stats/daily-metrics":
		h.handleDailyMetrics(w, r)
	case "api/stats/daily-metrics/sources":
		h.handleDailyMetricSources(w, r)
	case "api/stats/daily-metrics/sources/quality":
		h.handleDailyMetricSourceQuality(w, r)
	case "api/stats/daily-metrics/sources/selection":
		h.handleDailyMetricSourceSelection(w, r)
	case "api/stats/daily-metrics/diagnostics":
		h.handleDailyMetricDiagnostics(w, r)
	case "api/stats/daily-metrics/diagnostics/summary":
		h.handleDailyMetricDiagnosisSummary(w, r)
	case "api/stats/daily-metrics/diagnostics/reasons":
		h.handleDailyMetricDiagnosisReasonSummary(w, r)
	case "api/stats/daily-metrics/diagnostics/field-status":
		h.handleDailyMetricDiagnosisFieldStatusSummary(w, r)
	default:
		writeMetricError(w, http.StatusNotFound, "route not found")
	}
}

func (h *MetricHandler) handleDailyMetrics(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	var total int64
	if query.IncludeTotal {
		total, err = h.repository.Count(r.Context(), query)
		if err != nil {
			writeMetricError(w, http.StatusInternalServerError, err.Error())
			return
		}
	}
	rows, err := h.repository.Query(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if !query.IncludeTotal {
		total = int64(len(rows))
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":  rows,
		"total":  total,
		"limit":  query.Limit,
		"offset": query.Offset,
	})
}

func (h *MetricHandler) handleDailyMetricSources(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricSourceQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	var total int64
	if query.IncludeTotal {
		total, err = h.repository.CountSources(r.Context(), query)
		if err != nil {
			writeMetricError(w, http.StatusInternalServerError, err.Error())
			return
		}
	}
	rows, err := h.repository.QuerySources(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if !query.IncludeTotal {
		total = int64(len(rows))
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":  rows,
		"total":  total,
		"limit":  query.Limit,
		"offset": query.Offset,
	})
}

func (h *MetricHandler) handleDailyMetricSourceQuality(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricSourceQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	var total int64
	if query.IncludeTotal {
		total, err = h.repository.CountSourceQualitySummary(r.Context(), query)
		if err != nil {
			writeMetricError(w, http.StatusInternalServerError, err.Error())
			return
		}
	}
	rows, err := h.repository.QuerySourceQualitySummary(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if !query.IncludeTotal {
		total = int64(len(rows))
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":  rows,
		"total":  total,
		"limit":  query.Limit,
		"offset": query.Offset,
	})
}

func (h *MetricHandler) handleDailyMetricSourceSelection(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricSourceQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	var total int64
	if query.IncludeTotal {
		total, err = h.repository.CountSourceSelectionSummary(r.Context(), query)
		if err != nil {
			writeMetricError(w, http.StatusInternalServerError, err.Error())
			return
		}
	}
	rows, err := h.repository.QuerySourceSelectionSummary(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if !query.IncludeTotal {
		total = int64(len(rows))
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":  rows,
		"total":  total,
		"limit":  query.Limit,
		"offset": query.Offset,
	})
}

func (h *MetricHandler) handleDailyMetricDiagnostics(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricDiagnosisQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	var total int64
	if query.IncludeTotal {
		total, err = h.repository.CountDiagnostics(r.Context(), query)
		if err != nil {
			writeMetricError(w, http.StatusInternalServerError, err.Error())
			return
		}
	}
	rows, err := h.repository.QueryDiagnostics(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	if !query.IncludeTotal {
		total = int64(len(rows))
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":  rows,
		"total":  total,
		"limit":  query.Limit,
		"offset": query.Offset,
	})
}

func (h *MetricHandler) handleDailyMetricDiagnosisSummary(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricDiagnosisSummaryQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	rows, err := h.repository.QueryDiagnosisSummary(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	var totalActive int64
	var totalActionable int64
	for _, row := range rows {
		totalActive += row.ActiveCount
		totalActionable += row.ActionableIssueCount
	}
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":                  rows,
		"total":                  len(rows),
		"active_total":           totalActive,
		"actionable_issue_total": totalActionable,
	})
}

func (h *MetricHandler) handleDailyMetricDiagnosisReasonSummary(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricDiagnosisReasonSummaryQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	rows, err := h.repository.QueryDiagnosisReasonSummary(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	totalVehicles, totalActionable, pipelineIssues, sourceDataIssues := metricDiagnosisReasonSummaryIssueTotals(rows)
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":                   rows,
		"total":                   len(rows),
		"vehicle_total":           totalVehicles,
		"actionable_issue_total":  totalActionable,
		"pipeline_issue_total":    pipelineIssues,
		"source_data_issue_total": sourceDataIssues,
	})
}

func (h *MetricHandler) handleDailyMetricDiagnosisFieldStatusSummary(w http.ResponseWriter, r *http.Request) {
	query, err := parseMetricDiagnosisFieldStatusSummaryQuery(r)
	if err != nil {
		writeMetricError(w, http.StatusBadRequest, err.Error())
		return
	}
	rows, err := h.repository.QueryDiagnosisFieldStatusSummary(r.Context(), query)
	if err != nil {
		writeMetricError(w, http.StatusInternalServerError, err.Error())
		return
	}
	totalVehicles, totalActionable, pipelineIssues, sourceDataIssues := metricDiagnosisFieldStatusSummaryIssueTotals(rows)
	w.Header().Set("Content-Type", "application/json")
	_ = json.NewEncoder(w).Encode(map[string]any{
		"items":                   rows,
		"total":                   len(rows),
		"vehicle_total":           totalVehicles,
		"actionable_issue_total":  totalActionable,
		"pipeline_issue_total":    pipelineIssues,
		"source_data_issue_total": sourceDataIssues,
	})
}

func parseMetricQuery(r *http.Request) (MetricQuery, error) {
	values := r.URL.Query()
	limit, err := parseBoundedInt(values.Get("limit"), 50, 1, 1000, "limit")
	if err != nil {
		return MetricQuery{}, err
	}
	offset, err := parseBoundedInt(values.Get("offset"), 0, 0, 1_000_000, "offset")
	if err != nil {
		return MetricQuery{}, err
	}
	query := MetricQuery{
		VIN:          values.Get("vin"),
		Protocol:     values.Get("protocol"),
		IncludeTotal: strings.EqualFold(strings.TrimSpace(values.Get("includeTotal")), "true"),
		DateFrom:     values.Get("dateFrom"),
		DateTo:       values.Get("dateTo"),
		Limit:        limit,
		Offset:       offset,
	}
	if !validDate(query.DateFrom) || !validDate(query.DateTo) {
		return MetricQuery{}, errors.New("dateFrom/dateTo must use YYYY-MM-DD")
	}
	return normalizeMetricQuery(query), nil
}

func parseMetricSourceQuery(r *http.Request) (MetricSourceQuery, error) {
	values := r.URL.Query()
	limit, err := parseBoundedInt(values.Get("limit"), 50, 1, 1000, "limit")
	if err != nil {
		return MetricSourceQuery{}, err
	}
	offset, err := parseBoundedInt(values.Get("offset"), 0, 0, 1_000_000, "offset")
	if err != nil {
		return MetricSourceQuery{}, err
	}
	selected, err := parseOptionalBool(values.Get("selected"))
	if err != nil {
		return MetricSourceQuery{}, errors.New("selected must be true or false")
	}
	query := MetricSourceQuery{
		VIN:           values.Get("vin"),
		Protocol:      values.Get("protocol"),
		DateFrom:      values.Get("dateFrom"),
		DateTo:        values.Get("dateTo"),
		SourceIP:      values.Get("sourceIP"),
		QualityStatus: values.Get("qualityStatus"),
		QualityReason: values.Get("qualityReason"),
		Selected:      selected,
		OrderBy:       values.Get("orderBy"),
		IncludeTotal:  strings.EqualFold(strings.TrimSpace(values.Get("includeTotal")), "true"),
		Limit:         limit,
		Offset:        offset,
	}
	if !validDate(query.DateFrom) || !validDate(query.DateTo) {
		return MetricSourceQuery{}, errors.New("dateFrom/dateTo must use YYYY-MM-DD")
	}
	return normalizeMetricSourceQuery(query), nil
}

func parseMetricDiagnosisQuery(r *http.Request) (MetricDiagnosisQuery, error) {
	values := r.URL.Query()
	limit, err := parseBoundedInt(values.Get("limit"), 50, 1, 1000, "limit")
	if err != nil {
		return MetricDiagnosisQuery{}, err
	}
	offset, err := parseBoundedInt(values.Get("offset"), 0, 0, 1_000_000, "offset")
	if err != nil {
		return MetricDiagnosisQuery{}, err
	}
	query := MetricDiagnosisQuery{
		VIN:          values.Get("vin"),
		Protocol:     values.Get("protocol"),
		Date:         values.Get("date"),
		Diagnosis:    values.Get("diagnosis"),
		Reason:       values.Get("reason"),
		Severity:     values.Get("severity"),
		IncludeTotal: strings.EqualFold(strings.TrimSpace(values.Get("includeTotal")), "true"),
		Limit:        limit,
		Offset:       offset,
	}
	query = normalizeMetricDiagnosisQuery(query)
	if !validDate(query.Date) {
		return MetricDiagnosisQuery{}, errors.New("date must use YYYY-MM-DD")
	}
	if !validMetricDiagnosis(query.Diagnosis) {
		return MetricDiagnosisQuery{}, errors.New("diagnosis must be OK, MISSING_DAILY, NO_SOURCE_SAMPLE, or NO_TOTAL_MILEAGE")
	}
	if !validMetricDiagnosisReason(query.Reason) {
		return MetricDiagnosisQuery{}, errors.New("reason is not a supported daily mileage diagnosis reason")
	}
	if !validMetricDiagnosisSeverity(query.Severity) {
		return MetricDiagnosisQuery{}, errors.New("severity must be ok, pipeline, or source_data")
	}
	return query, nil
}

func parseMetricDiagnosisSummaryQuery(r *http.Request) (MetricDiagnosisQuery, error) {
	values := r.URL.Query()
	query := normalizeMetricDiagnosisQuery(MetricDiagnosisQuery{
		VIN:      values.Get("vin"),
		Protocol: values.Get("protocol"),
		Date:     values.Get("date"),
	})
	if !validDate(query.Date) {
		return MetricDiagnosisQuery{}, errors.New("date must use YYYY-MM-DD")
	}
	return query, nil
}

func parseMetricDiagnosisReasonSummaryQuery(r *http.Request) (MetricDiagnosisQuery, error) {
	values := r.URL.Query()
	query := normalizeMetricDiagnosisQuery(MetricDiagnosisQuery{
		VIN:       values.Get("vin"),
		Protocol:  values.Get("protocol"),
		Date:      values.Get("date"),
		Diagnosis: values.Get("diagnosis"),
		Reason:    values.Get("reason"),
		Severity:  values.Get("severity"),
	})
	if !validDate(query.Date) {
		return MetricDiagnosisQuery{}, errors.New("date must use YYYY-MM-DD")
	}
	if !validMetricDiagnosis(query.Diagnosis) {
		return MetricDiagnosisQuery{}, errors.New("diagnosis must be OK, MISSING_DAILY, NO_SOURCE_SAMPLE, or NO_TOTAL_MILEAGE")
	}
	if !validMetricDiagnosisReason(query.Reason) {
		return MetricDiagnosisQuery{}, errors.New("reason is not a supported daily mileage diagnosis reason")
	}
	if !validMetricDiagnosisSeverity(query.Severity) {
		return MetricDiagnosisQuery{}, errors.New("severity must be ok, pipeline, or source_data")
	}
	return query, nil
}

func parseMetricDiagnosisFieldStatusSummaryQuery(r *http.Request) (MetricDiagnosisQuery, error) {
	values := r.URL.Query()
	query := normalizeMetricDiagnosisQuery(MetricDiagnosisQuery{
		VIN:       values.Get("vin"),
		Protocol:  values.Get("protocol"),
		Date:      values.Get("date"),
		Diagnosis: values.Get("diagnosis"),
		Reason:    values.Get("reason"),
		Severity:  values.Get("severity"),
	})
	if !validDate(query.Date) {
		return MetricDiagnosisQuery{}, errors.New("date must use YYYY-MM-DD")
	}
	if !validMetricDiagnosis(query.Diagnosis) {
		return MetricDiagnosisQuery{}, errors.New("diagnosis must be OK, MISSING_DAILY, NO_SOURCE_SAMPLE, or NO_TOTAL_MILEAGE")
	}
	if !validMetricDiagnosisReason(query.Reason) {
		return MetricDiagnosisQuery{}, errors.New("reason is not a supported daily mileage diagnosis reason")
	}
	if !validMetricDiagnosisSeverity(query.Severity) {
		return MetricDiagnosisQuery{}, errors.New("severity must be ok, pipeline, or source_data")
	}
	return query, nil
}

func parseBoundedInt(raw string, fallback int, min int, max int, name string) (int, error) {
	raw = strings.TrimSpace(raw)
	if raw == "" {
		return fallback, nil
	}
	value, err := strconv.Atoi(raw)
	if err != nil || value < min || value > max {
		return 0, fmt.Errorf("%s must be between %d and %d", name, min, max)
	}
	return value, nil
}

func validDate(value string) bool {
	value = strings.TrimSpace(value)
	if value == "" {
		return true
	}
	if len(value) != len("2006-01-02") {
		return false
	}
	for i, r := range value {
		switch i {
		case 4, 7:
			if r != '-' {
				return false
			}
		default:
			if r < '0' || r > '9' {
				return false
			}
		}
	}
	return true
}

func validMetricDiagnosis(value string) bool {
	switch strings.ToUpper(strings.TrimSpace(value)) {
	case "", "OK", "MISSING_DAILY", "NO_SOURCE_SAMPLE", "NO_TOTAL_MILEAGE":
		return true
	default:
		return false
	}
}

func validMetricDiagnosisReason(value string) bool {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "",
		"daily_metric_exists",
		"source_samples_all_invalid",
		"source_samples_all_excluded",
		"source_samples_exist_but_final_metric_missing",
		"realtime_location_has_total_mileage_but_no_stat_sample",
		"realtime_total_mileage_missing",
		"realtime_total_mileage_non_positive",
		"realtime_total_mileage_time_missing",
		"realtime_total_mileage_not_reported_on_stat_date",
		"realtime_active_without_total_mileage":
		return true
	default:
		return false
	}
}

func validMetricDiagnosisSeverity(value string) bool {
	switch strings.ToLower(strings.TrimSpace(value)) {
	case "", metricDiagnosisSeverityOK, metricDiagnosisSeverityPipeline, metricDiagnosisSeveritySourceData:
		return true
	default:
		return false
	}
}

func classifyMetricDiagnosis(hasDailyMetric bool, sourceSampleCount int64, okSourceCount int64, selectableSourceCount int64, realtimeTotal sql.NullFloat64, totalMileageAt string, statDate string) (string, string) {
	if hasDailyMetric {
		return "OK", "daily_metric_exists"
	}
	if sourceSampleCount > 0 {
		if okSourceCount == 0 {
			return "MISSING_DAILY", "source_samples_all_invalid"
		}
		if selectableSourceCount == 0 {
			return "MISSING_DAILY", "source_samples_all_excluded"
		}
		return "MISSING_DAILY", "source_samples_exist_but_final_metric_missing"
	}
	if hasUsableRealtimeTotalMileage(realtimeTotal, totalMileageAt, statDate) {
		return "NO_SOURCE_SAMPLE", "realtime_location_has_total_mileage_but_no_stat_sample"
	}
	if !realtimeTotal.Valid {
		return "NO_TOTAL_MILEAGE", "realtime_total_mileage_missing"
	}
	if realtimeTotal.Float64 <= 0 {
		return "NO_TOTAL_MILEAGE", "realtime_total_mileage_non_positive"
	}
	if strings.TrimSpace(totalMileageAt) == "" {
		return "NO_TOTAL_MILEAGE", "realtime_total_mileage_time_missing"
	}
	if !strings.HasPrefix(totalMileageAt, statDate) {
		return "NO_TOTAL_MILEAGE", "realtime_total_mileage_not_reported_on_stat_date"
	}
	return "NO_TOTAL_MILEAGE", "realtime_active_without_total_mileage"
}

func hasUsableRealtimeTotalMileage(total sql.NullFloat64, totalMileageAt string, statDate string) bool {
	return total.Valid && total.Float64 > 0 && strings.HasPrefix(totalMileageAt, statDate)
}

func splitMetricStatuses(value string) []string {
	value = strings.TrimSpace(value)
	if value == "" {
		return nil
	}
	parts := strings.Split(value, ",")
	out := make([]string, 0, len(parts))
	for _, part := range parts {
		part = strings.TrimSpace(part)
		if part != "" {
			out = append(out, part)
		}
	}
	return out
}

func writeMetricError(w http.ResponseWriter, status int, message string) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)
	_ = json.NewEncoder(w).Encode(map[string]any{"error": message})
}

type scanDate struct {
	String string
}

func (s *scanDate) Scan(value any) error {
	s.String = formatSQLTime(value, "2006-01-02")
	return nil
}

type scanDateTime struct {
	String string
}

func (s *scanDateTime) Scan(value any) error {
	s.String = formatSQLTime(value, "2006-01-02 15:04:05")
	return nil
}

func formatSQLTime(value any, layout string) string {
	switch typed := value.(type) {
	case nil:
		return ""
	case time.Time:
		return typed.Format(layout)
	case []byte:
		return strings.TrimSpace(string(typed))
	case string:
		return strings.TrimSpace(typed)
	default:
		return strings.TrimSpace(fmt.Sprint(typed))
	}
}
