package telemetry

import (
	"encoding/json"
	"strconv"
	"strings"

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

type MileageFieldMapping struct {
	Key   string
	Scale float64
}

// MileageFieldMappings is the single protocol-to-unit contract used by every
// projection that exposes or calculates total mileage.
func MileageFieldMappings(protocol envelope.Protocol) []MileageFieldMapping {
	switch protocol {
	case envelope.ProtocolGB32960:
		return []MileageFieldMapping{{Key: "gb32960.vehicle.total_mileage_km", Scale: 1}}
	case envelope.ProtocolJT808:
		return []MileageFieldMapping{{Key: "jt808.location.total_mileage_km", Scale: 1}}
	case envelope.ProtocolYutongMQTT:
		return []MileageFieldMapping{
			{Key: "yutong_mqtt.data.total_mileage_km", Scale: 1},
			{Key: "yutong_mqtt.root.data.total_mileage_km", Scale: 1},
			{Key: "yutong_mqtt.data.total_mileage", Scale: 0.001},
			{Key: "yutong_mqtt.root.data.total_mileage", Scale: 0.001},
		}
	default:
		return nil
	}
}

func TotalMileageKM(protocol envelope.Protocol, fields map[string]any) (float64, bool) {
	for _, mapping := range MileageFieldMappings(protocol) {
		value, ok := Number(fields, mapping.Key)
		if !ok {
			continue
		}
		return value * mapping.Scale, true
	}
	return 0, false
}

func Number(fields map[string]any, key string) (float64, bool) {
	if len(fields) == 0 {
		return 0, false
	}
	value, ok := fields[key]
	if !ok || value == nil {
		return 0, false
	}
	switch typed := value.(type) {
	case float64:
		return typed, true
	case float32:
		return float64(typed), true
	case int:
		return float64(typed), true
	case int8:
		return float64(typed), true
	case int16:
		return float64(typed), true
	case int32:
		return float64(typed), true
	case int64:
		return float64(typed), true
	case uint:
		return float64(typed), true
	case uint8:
		return float64(typed), true
	case uint16:
		return float64(typed), true
	case uint32:
		return float64(typed), true
	case uint64:
		return float64(typed), true
	case json.Number:
		parsed, err := typed.Float64()
		return parsed, err == nil
	case string:
		parsed, err := strconv.ParseFloat(strings.TrimSpace(typed), 64)
		return parsed, err == nil
	default:
		return 0, false
	}
}
