"""Mountain Twin Sun Engine v0.1 — deterministic astronomical solar position.
Does NOT include terrain horizon/shadows. Golden Hour convention in v0.1: solar elevation -4°..+6°.
"""

import math
from datetime import datetime, timezone


def solar_position(dt_local: datetime, lat: float, lon: float) -> tuple[float, float]:
    """Return geometric (elevation, azimuth) in degrees for an aware datetime.

    Coordinates are degrees north/east. Azimuth is clockwise from north in
    [0, 360). This approximate model excludes refraction and terrain shadows.
    Naive datetimes are rejected to avoid dependence on the host timezone.
    """
    if dt_local.utcoffset() is None:
        raise ValueError("dt_local must be timezone-aware")
    dt_utc = dt_local.astimezone(timezone.utc)
    n = dt_utc.timetuple().tm_yday
    hour = dt_utc.hour + dt_utc.minute / 60 + dt_utc.second / 3600
    gamma = 2 * math.pi / 365 * (n - 1 + (hour - 12) / 24)
    eqtime = 229.18 * (
        0.000075
        + 0.001868 * math.cos(gamma)
        - 0.032077 * math.sin(gamma)
        - 0.014615 * math.cos(2 * gamma)
        - 0.040849 * math.sin(2 * gamma)
    )
    decl = (
        0.006918
        - 0.399912 * math.cos(gamma)
        + 0.070257 * math.sin(gamma)
        - 0.006758 * math.cos(2 * gamma)
        + 0.000907 * math.sin(2 * gamma)
        - 0.002697 * math.cos(3 * gamma)
        + 0.00148 * math.sin(3 * gamma)
    )
    tzmin = dt_local.utcoffset().total_seconds() / 60
    tst = (
        dt_local.hour * 60 + dt_local.minute + dt_local.second / 60 + eqtime + 4 * lon - tzmin
    ) % 1440
    ha = math.radians(tst / 4 - 180)
    phi = math.radians(lat)
    cosz = math.sin(phi) * math.sin(decl) + math.cos(phi) * math.cos(decl) * math.cos(ha)
    zen = math.acos(max(-1, min(1, cosz)))
    elev = 90 - math.degrees(zen)
    az = (
        math.degrees(
            math.atan2(math.sin(ha), math.cos(ha) * math.sin(phi) - math.tan(decl) * math.cos(phi))
        )
        + 180
    ) % 360
    return elev, az
