"""Mountain Twin Trail Engine v0.1.
Geometry primitives for future GPX point/route analytics.
Important: GPX timestamps must be validated before interpreting them as actual hike times.
"""

from math import asin, atan2, cos, degrees, radians, sin, sqrt

EARTH_RADIUS_M = 6371008.8


def haversine_m(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    """Return spherical surface distance in metres; coordinates are degrees."""
    p1, p2 = radians(lat1), radians(lat2)
    dp, dl = radians(lat2 - lat1), radians(lon2 - lon1)
    a = sin(dp / 2) ** 2 + cos(p1) * cos(p2) * sin(dl / 2) ** 2
    return 2 * EARTH_RADIUS_M * asin(min(1, sqrt(a)))


def bearing_deg(lat1: float, lon1: float, lat2: float, lon2: float) -> float:
    """Return initial bearing clockwise from north in [0, 360) degrees.

    Coordinates are degrees. Coincident points return 0 by convention;
    bearings at coincident or antipodal points are geometrically undefined.
    """
    p1, p2 = radians(lat1), radians(lat2)
    dl = radians(lon2 - lon1)
    y = sin(dl) * cos(p2)
    x = cos(p1) * sin(p2) - sin(p1) * cos(p2) * cos(dl)
    return (degrees(atan2(y, x)) + 360) % 360
