"""Greenwich Mean Sidereal Time (GMST) conversions.

GMST is the hour angle of the mean vernal equinox measured from the Greenwich
meridian. It represents Earth's rotational position relative to the stars and
is essential for transforming between Earth-Centered Earth-Fixed (ECEF) and
Earth-Centered Inertial (ECI) coordinate systems.

The implementation uses the IAU 1982 model for GMST, which provides accuracy
suitable for most applications (sub-arcsecond for dates within a few decades
of J2000.0).

References:
    - IERS Conventions (2010), Chapter 5
    - Astronomical Algorithms, Jean Meeus (2nd ed.)
    - USNO Circular 179
"""

from __future__ import annotations

from typing import TYPE_CHECKING

import numpy as np

from .julian import JD_J2000, datetime_to_jd, unix_to_jd

if TYPE_CHECKING:
    from collections.abc import Sequence
    from datetime import datetime

    from numpy.typing import NDArray

# Seconds per sidereal day (Earth rotation period relative to stars)
SECONDS_PER_SIDEREAL_DAY = 86164.090530833

# Earth rotation rate in radians per second
EARTH_ROTATION_RATE = 2.0 * np.pi / SECONDS_PER_SIDEREAL_DAY


def jd_to_gmst(
    jd: float | NDArray[np.floating] | Sequence[float],
) -> NDArray[np.floating]:
    """Convert Julian Date to Greenwich Mean Sidereal Time.

    Uses the IAU 2006 formula for Earth Rotation Angle (ERA) combined with
    the equation of the origins to compute GMST. This provides sub-arcsecond
    accuracy for dates within a few decades of J2000.0.

    Args:
        jd: Julian Date (UT1). Can be a scalar, sequence, or array.

    Returns:
        GMST in radians [0, 2*pi) as numpy array.

    References:
        - IERS Conventions (2010), Chapter 5
        - Capitaine et al. (2003), Astronomy & Astrophysics 412, 567-586

    Examples:
        >>> jd_to_gmst(2451545.0)  # J2000.0 epoch
        array(4.89496...)
    """
    jd_arr = np.asarray(jd, dtype=np.float64)

    # Days since J2000.0 (including fractional days)
    d = jd_arr - JD_J2000

    # Julian centuries since J2000.0 for the polynomial terms
    t = d / 36525.0

    # GMST in radians using the IAU 2006 precession model
    # GMST = ERA + polynomial in T
    # where ERA = 2*pi*(0.7790572732640 + 1.00273781191135448 * Du)
    # and the polynomial accounts for precession

    # Earth Rotation Angle (ERA)
    era = 2.0 * np.pi * (0.7790572732640 + 1.00273781191135448 * d)

    # Polynomial correction for precession (converts ERA to GMST)
    # Coefficients from IAU 2006 precession model (in radians)
    gmst_poly = (
        0.014506  # arcsec, converted below
        + 4612.156534 * t
        + 1.3915817 * t**2
        - 0.00000044 * t**3
        - 0.000029956 * t**4
        - 0.0000000368 * t**5
    )
    # Convert arcseconds to radians
    gmst_poly_rad = gmst_poly * (np.pi / 648000.0)  # arcsec to radians

    gmst_rad = era + gmst_poly_rad

    # Normalize to [0, 2*pi)
    gmst_rad = gmst_rad % (2.0 * np.pi)

    return gmst_rad  # noqa: RET504


def unix_to_gmst(
    unix_s: float | NDArray[np.floating] | Sequence[float],
) -> NDArray[np.floating]:
    """Convert Unix timestamp to Greenwich Mean Sidereal Time.

    Args:
        unix_s: Unix timestamp in seconds. Can be a scalar, sequence, or array.

    Returns:
        GMST in radians [0, 2*pi) as numpy array.

    Examples:
        >>> unix_to_gmst(946728000.0)  # 2000-01-01 12:00:00 UTC (J2000.0)
        array(4.89496...)
    """
    jd = unix_to_jd(unix_s)
    return jd_to_gmst(jd)


def datetime_to_gmst(dt: datetime) -> float:
    """Convert a datetime object to Greenwich Mean Sidereal Time.

    The datetime is converted to UTC if timezone-aware, or assumed to be UTC
    if naive.

    Args:
        dt: A datetime object. If timezone-naive, assumed to be UTC.

    Returns:
        GMST in radians [0, 2*pi).

    Examples:
        >>> from datetime import datetime, UTC
        >>> datetime_to_gmst(datetime(2000, 1, 1, 12, 0, 0, tzinfo=UTC))
        4.89496...
    """
    jd = datetime_to_jd(dt)
    return float(jd_to_gmst(jd))
