"""Shared GPS accuracy helpers (keep in sync with Arduino MIN_SATELLITES)."""

import math

MIN_STRONG_SATELLITES = 5
MAX_STATIONARY_SPEED_KMH = 2.0
MAX_STATIONARY_JUMP_METERS = 40.0


def _distance_meters(lat1: float, lng1: float, lat2: float, lng2: float) -> float:
    radius = 6371000.0
    d_lat = math.radians(lat2 - lat1)
    d_lng = math.radians(lng2 - lng1)
    a = (
        math.sin(d_lat / 2) ** 2
        + math.cos(math.radians(lat1))
        * math.cos(math.radians(lat2))
        * math.sin(d_lng / 2) ** 2
    )
    return radius * 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))


def _is_stationary_jitter(point, previous) -> bool:
    if previous is None:
        return False
    if point.latitude is None or point.longitude is None:
        return False
    if previous.latitude is None or previous.longitude is None:
        return False

    speed = point.speed if point.speed is not None else 0.0
    prev_speed = previous.speed if previous.speed is not None else 0.0
    if speed > MAX_STATIONARY_SPEED_KMH or prev_speed > MAX_STATIONARY_SPEED_KMH:
        return False

    jump = _distance_meters(
        float(previous.latitude),
        float(previous.longitude),
        float(point.latitude),
        float(point.longitude),
    )
    return jump > MAX_STATIONARY_JUMP_METERS


def classify_points(points):
    """Return dict mapping point.id -> 'strong' | 'weak' | 'jitter' | 'none'."""
    classifications = {}
    last_strong = None

    for point in reversed(list(points)):
        if not point.fix_valid or point.latitude is None or point.longitude is None:
            classifications[point.id] = "none"
            continue

        satellites = point.satellites or 0
        if satellites < MIN_STRONG_SATELLITES:
            classifications[point.id] = "weak"
            continue

        if _is_stationary_jitter(point, last_strong):
            classifications[point.id] = "jitter"
            continue

        classifications[point.id] = "strong"
        last_strong = point

    return classifications


def point_quality(point, classifications=None, points=None) -> str:
    if classifications is not None:
        return classifications.get(point.id, "none")

    if points is not None:
        return classify_points(points).get(point.id, "none")

    if not point.fix_valid or point.latitude is None or point.longitude is None:
        return "none"
    satellites = point.satellites or 0
    if satellites >= MIN_STRONG_SATELLITES:
        return "strong"
    return "weak"


def quality_label(quality: str) -> str:
    return {
        "strong": "Accurate",
        "weak": "Weak",
        "jitter": "GPS drift",
        "none": "No fix",
    }.get(quality, "Unknown")
