package platform

import (
	"context"
	"database/sql"
	"fmt"
	"sort"
	"strings"
)

type canonicalLocationEvidence struct {
	row VehicleLocationSourceEvidence
}

func (s *ProductionStore) VehicleSourceEvidence(ctx context.Context, vin string, date string) (VehicleSourceEvidence, error) {
	evidence := VehicleSourceEvidence{
		VIN:             vin,
		MileageDate:     date,
		LocationSources: []VehicleLocationSourceEvidence{},
		MileageSources:  []VehicleMileageSourceEvidence{},
	}
	_ = s.db.QueryRowContext(ctx, `SELECT COALESCE(MAX(NULLIF(plate, '')), '') FROM vehicle_identity_binding WHERE vin = ?`, vin).Scan(&evidence.Plate)

	canonical, err := s.canonicalLocationEvidence(ctx, vin)
	if err != nil {
		return VehicleSourceEvidence{}, err
	}
	candidates, err := s.locationSourceEvidence(ctx, vin, canonical)
	if err != nil {
		return VehicleSourceEvidence{}, err
	}
	seen := make(map[string]struct{}, len(candidates))
	for _, source := range candidates {
		seen[source.Protocol+"\x00"+source.sourceKey] = struct{}{}
		evidence.LocationSources = append(evidence.LocationSources, source)
	}
	for _, source := range canonical {
		key := source.row.Protocol + "\x00" + source.row.sourceKey
		if _, exists := seen[key]; exists {
			continue
		}
		evidence.LocationSources = append(evidence.LocationSources, source.row)
	}
	sortLocationEvidence(evidence.LocationSources)

	mileageSources, err := s.mileageSourceEvidence(ctx, vin, date)
	if err != nil {
		return VehicleSourceEvidence{}, err
	}
	canonicalMileage, err := s.canonicalMileageEvidence(ctx, vin, date)
	if err != nil {
		return VehicleSourceEvidence{}, err
	}
	selectedProtocols := map[string]bool{}
	for _, source := range mileageSources {
		if source.SelectedWithinProtocol {
			selectedProtocols[source.Protocol] = true
		}
		evidence.MileageSources = append(evidence.MileageSources, source)
	}
	for _, source := range canonicalMileage {
		if selectedProtocols[source.Protocol] {
			continue
		}
		evidence.MileageSources = append(evidence.MileageSources, source)
	}
	sortMileageEvidence(evidence.MileageSources)
	return evidence, nil
}

func (s *ProductionStore) canonicalLocationEvidence(ctx context.Context, vin string) ([]canonicalLocationEvidence, error) {
	rows, err := s.db.QueryContext(ctx, `SELECT protocol, COALESCE(NULLIF(source_key, ''), CONCAT(protocol, ':canonical')),
COALESCE(NULLIF(plate, ''), ''), DATE_FORMAT(event_time, '%Y-%m-%d %H:%i:%s'),
latitude, longitude, speed_kmh, total_mileage_km, soc_percent,
DATE_FORMAT(received_at, '%Y-%m-%d %H:%i:%s'),
CASE WHEN received_at >= DATE_SUB(NOW(), INTERVAL 2 MINUTE) THEN 1 ELSE 0 END
FROM vehicle_realtime_location
WHERE vin = ?
ORDER BY protocol`, vin)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	out := make([]canonicalLocationEvidence, 0, 3)
	for rows.Next() {
		var row VehicleLocationSourceEvidence
		var plate string
		var eventTime, receivedAt sql.NullString
		var latitude, longitude, speed, mileage, soc sql.NullFloat64
		var online int
		if err := rows.Scan(
			&row.Protocol, &row.sourceKey, &plate, &eventTime,
			&latitude, &longitude, &speed, &mileage, &soc, &receivedAt, &online,
		); err != nil {
			return nil, err
		}
		row.SourceLabel = row.Protocol
		row.SourceKind = "CANONICAL"
		row.SelectedWithinProtocol = true
		row.Enabled = true
		row.Online = online == 1
		row.QualityStatus = "OK"
		row.Longitude = nullFloatPointer(longitude)
		row.Latitude = nullFloatPointer(latitude)
		row.SpeedKmh = nullFloatPointer(speed)
		row.TotalMileageKm = nullFloatPointer(mileage)
		row.SOCPercent = nullFloatPointer(soc)
		row.EventTime = nullString(eventTime)
		row.ReceivedAt = nullString(receivedAt)
		out = append(out, canonicalLocationEvidence{row: row})
	}
	return out, rows.Err()
}

func (s *ProductionStore) locationSourceEvidence(ctx context.Context, vin string, canonical []canonicalLocationEvidence) ([]VehicleLocationSourceEvidence, error) {
	selected := make(map[string]string, len(canonical))
	for _, source := range canonical {
		selected[source.row.Protocol] = source.row.sourceKey
	}
	rows, err := s.db.QueryContext(ctx, `SELECT s.protocol, s.source_key, s.source_code, s.source_kind, s.phone, s.device_id,
DATE_FORMAT(s.event_time, '%Y-%m-%d %H:%i:%s'), s.latitude, s.longitude, s.speed_kmh,
s.total_mileage_km, s.soc_percent, DATE_FORMAT(s.received_at, '%Y-%m-%d %H:%i:%s'),
s.quality_status, s.quality_reason,
COALESCE(p.enabled, 1),
COALESCE(p.priority, CASE s.source_kind WHEN 'DIRECT' THEN 20 WHEN 'PLATFORM' THEN 30 ELSE 40 END),
CASE WHEN s.received_at >= DATE_SUB(NOW(), INTERVAL 2 MINUTE) THEN 1 ELSE 0 END
FROM vehicle_realtime_location_source s
LEFT JOIN vehicle_location_source_policy p
  ON p.vin = s.vin AND p.protocol = s.protocol AND p.source_key = s.source_key
WHERE s.vin = ?
ORDER BY s.protocol, COALESCE(p.enabled, 1) DESC,
COALESCE(p.priority, CASE s.source_kind WHEN 'DIRECT' THEN 20 WHEN 'PLATFORM' THEN 30 ELSE 40 END),
s.received_at DESC
LIMIT 50`, vin)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	out := make([]VehicleLocationSourceEvidence, 0)
	for rows.Next() {
		var row VehicleLocationSourceEvidence
		var sourceCode, phone, deviceID string
		var eventTime, receivedAt, qualityReason sql.NullString
		var latitude, longitude, speed, mileage, soc sql.NullFloat64
		var enabled, online int
		if err := rows.Scan(
			&row.Protocol, &row.sourceKey, &sourceCode, &row.SourceKind, &phone, &deviceID,
			&eventTime, &latitude, &longitude, &speed, &mileage, &soc, &receivedAt,
			&row.QualityStatus, &qualityReason, &enabled, &row.Priority, &online,
		); err != nil {
			return nil, err
		}
		row.TerminalLabel = terminalEvidenceLabel(phone, deviceID)
		row.SourceLabel = readableSourceLabel(row.Protocol, sourceCode, row.SourceKind, row.TerminalLabel)
		row.SelectedWithinProtocol = selected[row.Protocol] == row.sourceKey
		row.Enabled = enabled == 1
		row.Online = online == 1
		row.QualityReason = nullString(qualityReason)
		row.Longitude = nullFloatPointer(longitude)
		row.Latitude = nullFloatPointer(latitude)
		row.SpeedKmh = nullFloatPointer(speed)
		row.TotalMileageKm = nullFloatPointer(mileage)
		row.SOCPercent = nullFloatPointer(soc)
		row.EventTime = nullString(eventTime)
		row.ReceivedAt = nullString(receivedAt)
		out = append(out, row)
	}
	return out, rows.Err()
}

func (s *ProductionStore) mileageSourceEvidence(ctx context.Context, vin string, date string) ([]VehicleMileageSourceEvidence, error) {
	rows, err := s.db.QueryContext(ctx, `SELECT s.protocol, s.source_key,
COALESCE(NULLIF(TRIM(s.platform_name), ''), NULLIF(TRIM(ds.platform_name), ''), NULLIF(TRIM(ds.source_code), ''), ''),
COALESCE(NULLIF(TRIM(ds.source_kind), ''), 'UNKNOWN'),
COALESCE(s.phone, ''), COALESCE(s.device_id, ''),
s.first_total_mileage_km, s.latest_total_mileage_km, s.daily_mileage_km, s.sample_count,
DATE_FORMAT(s.first_event_time, '%Y-%m-%d %H:%i:%s'),
DATE_FORMAT(s.latest_event_time, '%Y-%m-%d %H:%i:%s'),
s.quality_status, s.quality_reason, s.is_selected,
COALESCE(ds.enabled, 1), COALESCE(ds.trust_priority, 100)
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.vin = ? AND s.stat_date = ?
ORDER BY s.protocol, s.is_selected DESC, COALESCE(ds.enabled, 1) DESC,
COALESCE(ds.trust_priority, 100), s.sample_count DESC, s.latest_event_time DESC
LIMIT 100`, vin, date)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	out := make([]VehicleMileageSourceEvidence, 0)
	for rows.Next() {
		var row VehicleMileageSourceEvidence
		var provider, phone, deviceID string
		var firstTotal, latestTotal, daily sql.NullFloat64
		var firstTime, latestTime, qualityReason sql.NullString
		var selected, enabled int
		if err := rows.Scan(
			&row.Protocol, &row.sourceKey, &provider, &row.SourceKind, &phone, &deviceID,
			&firstTotal, &latestTotal, &daily, &row.SampleCount, &firstTime, &latestTime,
			&row.QualityStatus, &qualityReason, &selected, &enabled, &row.Priority,
		); err != nil {
			return nil, err
		}
		row.TerminalLabel = terminalEvidenceLabel(phone, deviceID)
		row.SourceLabel = readableSourceLabel(row.Protocol, provider, row.SourceKind, row.TerminalLabel)
		row.SelectedWithinProtocol = selected == 1
		row.Enabled = enabled == 1
		row.QualityReason = nullString(qualityReason)
		row.FirstTotalMileageKm = nullFloatPointer(firstTotal)
		row.LatestTotalMileageKm = nullFloatPointer(latestTotal)
		row.DailyMileageKm = nullFloatPointer(daily)
		row.FirstEventTime = nullString(firstTime)
		row.LatestEventTime = nullString(latestTime)
		out = append(out, row)
	}
	return out, rows.Err()
}

func (s *ProductionStore) canonicalMileageEvidence(ctx context.Context, vin string, date string) ([]VehicleMileageSourceEvidence, error) {
	rows, err := s.db.QueryContext(ctx, `SELECT m.protocol,
COALESCE(NULLIF(TRIM(ds.platform_name), ''), NULLIF(TRIM(ds.source_code), ''), ''),
COALESCE(NULLIF(TRIM(ds.source_kind), ''), 'CANONICAL'),
m.latest_total_mileage_km, m.daily_mileage_km,
DATE_FORMAT(m.updated_at, '%Y-%m-%d %H:%i:%s'),
COALESCE(ds.enabled, 1), COALESCE(ds.trust_priority, 100)
FROM vehicle_daily_mileage m
LEFT JOIN vehicle_data_source ds ON ds.id = m.source_id
WHERE m.vin = ? AND m.stat_date = ?
ORDER BY m.protocol`, vin, date)
	if err != nil {
		return nil, err
	}
	defer rows.Close()
	out := make([]VehicleMileageSourceEvidence, 0, 3)
	for rows.Next() {
		var row VehicleMileageSourceEvidence
		var provider string
		var latestTotal, daily sql.NullFloat64
		var latestTime sql.NullString
		var enabled int
		if err := rows.Scan(
			&row.Protocol, &provider, &row.SourceKind, &latestTotal, &daily,
			&latestTime, &enabled, &row.Priority,
		); err != nil {
			return nil, err
		}
		row.sourceKey = row.Protocol + ":canonical"
		row.SourceLabel = readableSourceLabel(row.Protocol, provider, row.SourceKind, "")
		row.SelectedWithinProtocol = true
		row.Enabled = enabled == 1
		row.QualityStatus = "OK"
		row.LatestTotalMileageKm = nullFloatPointer(latestTotal)
		row.DailyMileageKm = nullFloatPointer(daily)
		row.LatestEventTime = nullString(latestTime)
		out = append(out, row)
	}
	return out, rows.Err()
}

func readableSourceLabel(protocol string, provider string, sourceKind string, terminal string) string {
	if provider = strings.TrimSpace(provider); provider != "" {
		return provider
	}
	if terminal != "" {
		return protocol + " · " + terminal
	}
	if sourceKind = strings.TrimSpace(sourceKind); sourceKind != "" && sourceKind != "UNKNOWN" && sourceKind != "CANONICAL" {
		return protocol + " · " + sourceKind
	}
	return protocol
}

func terminalEvidenceLabel(phone string, deviceID string) string {
	parts := make([]string, 0, 2)
	if phone = maskEvidenceIdentifier(phone); phone != "" {
		parts = append(parts, "终端 "+phone)
	}
	if deviceID = maskEvidenceIdentifier(deviceID); deviceID != "" && (len(parts) == 0 || !strings.Contains(parts[0], deviceID)) {
		parts = append(parts, "设备 "+deviceID)
	}
	return strings.Join(parts, " / ")
}

func maskEvidenceIdentifier(value string) string {
	value = strings.TrimSpace(value)
	switch length := len([]rune(value)); {
	case length == 0:
		return ""
	case length <= 4:
		return "****"
	case length <= 7:
		runes := []rune(value)
		return string(runes[:2]) + "***" + string(runes[length-2:])
	default:
		runes := []rune(value)
		return string(runes[:3]) + "****" + string(runes[length-4:])
	}
}

func nullFloatPointer(value sql.NullFloat64) *float64 {
	if !value.Valid {
		return nil
	}
	result := value.Float64
	return &result
}

func nullString(value sql.NullString) string {
	if !value.Valid {
		return ""
	}
	return value.String
}

func protocolEvidenceRank(protocol string) int {
	for index, candidate := range canonicalVehicleProtocols {
		if protocol == candidate {
			return index
		}
	}
	return len(canonicalVehicleProtocols)
}

func sortLocationEvidence(sources []VehicleLocationSourceEvidence) {
	sort.SliceStable(sources, func(left, right int) bool {
		lhs, rhs := sources[left], sources[right]
		if protocolEvidenceRank(lhs.Protocol) != protocolEvidenceRank(rhs.Protocol) {
			return protocolEvidenceRank(lhs.Protocol) < protocolEvidenceRank(rhs.Protocol)
		}
		if lhs.SelectedWithinProtocol != rhs.SelectedWithinProtocol {
			return lhs.SelectedWithinProtocol
		}
		if lhs.Enabled != rhs.Enabled {
			return lhs.Enabled
		}
		if lhs.Priority != rhs.Priority {
			return lhs.Priority < rhs.Priority
		}
		return fmt.Sprintf("%s\x00%s", lhs.SourceLabel, lhs.TerminalLabel) < fmt.Sprintf("%s\x00%s", rhs.SourceLabel, rhs.TerminalLabel)
	})
}

func sortMileageEvidence(sources []VehicleMileageSourceEvidence) {
	sort.SliceStable(sources, func(left, right int) bool {
		lhs, rhs := sources[left], sources[right]
		if protocolEvidenceRank(lhs.Protocol) != protocolEvidenceRank(rhs.Protocol) {
			return protocolEvidenceRank(lhs.Protocol) < protocolEvidenceRank(rhs.Protocol)
		}
		if lhs.SelectedWithinProtocol != rhs.SelectedWithinProtocol {
			return lhs.SelectedWithinProtocol
		}
		if lhs.Enabled != rhs.Enabled {
			return lhs.Enabled
		}
		if lhs.Priority != rhs.Priority {
			return lhs.Priority < rhs.Priority
		}
		return fmt.Sprintf("%s\x00%s", lhs.SourceLabel, lhs.TerminalLabel) < fmt.Sprintf("%s\x00%s", rhs.SourceLabel, rhs.TerminalLabel)
	})
}
