from collections.abc import Callable
from typing import Literal, Self, overload
import numpy as np

type Scale = Literal["TAI", "TCB", "TCG", "TDB", "TT", "UT1"]
type Epoch = Literal["jd", "mjd", "j1950", "j2000"]
type Unit = Literal["seconds", "days", "centuries"]
type Vec3 = tuple[float, float, float]


def find_events(func: Callable[[float], float], start: Time, times: list[float]): ...


def find_windows(
        func: Callable[[float], float], start: Time, end: Time, times: list[float]
): ...


def visibility(
        times: list[Time],
        gs: GroundLocation,
        min_elevation: float,
        sc: Trajectory,
        provider: UT1Provider,
): ...


class Frame:
    def __new__(cls, abbreviation: str): ...

    def name(self) -> str: ...

    def abbreviation(self) -> str: ...


class State:
    def __new__(
            cls,
            time: Time,
            position: Vec3,
            velocity: Vec3,
            origin: Origin | None = None,
            frame: Frame | None = None,
    ): ...

    def time(self) -> Time: ...

    def origin(self) -> Origin: ...

    def reference_frame(self) -> Frame: ...

    def position(self) -> np.ndarray: ...

    def velocity(self) -> np.ndarray: ...

    def to_frame(self, frame: Frame) -> State: ...

    def to_keplerian(self) -> Keplerian: ...

    def rotation_lvlh(self) -> np.ndarray: ...

    def to_ground_location(self) -> GroundLocation: ...


class Keplerian:
    def __new__(
            cls,
            time: Time,
            semi_major_axis: float,
            eccentricity: float,
            inclination: float,
            longitude_of_ascending_node: float,
            argument_of_periapsis: float,
            true_anomaly: float,
            origin: Origin | None = None,
    ): ...

    def time(self) -> Time: ...

    def origin(self) -> Origin: ...

    def semi_major_axis(self) -> float: ...

    def eccentricity(self) -> float: ...

    def inclination(self) -> float: ...

    def longitude_of_ascending_node(self) -> float: ...

    def argument_of_periapsis(self) -> float: ...

    def true_anomaly(self) -> float: ...

    def to_cartesian(self) -> State: ...

    def orbital_period(self) -> TimeDelta: ...


class Trajectory:
    def __new__(cls, states: list[State]): ...

    @classmethod
    def from_numpy(
            cls,
            start_time: Time,
            states: np.ndarray,
            origin: Origin | None = None,
            frame: Frame | None = None,
    ) -> Self: ...

    def origin(self) -> Origin: ...

    def reference_frame(self) -> Frame: ...

    def to_numpy(self) -> np.ndarray: ...

    def states(self) -> list[State]: ...

    def find_events(self, func: Callable[[State], float]) -> list[Event]: ...

    def find_windows(self, func: Callable[[State], float]) -> list[Window]: ...

    def interpolate(self, time: Time) -> State: ...


class Event:
    def time(self) -> Time: ...

    def cross(self) -> str: ...


class Window:
    def start(self) -> Time: ...

    def end(self) -> Time: ...

    def duration(self) -> TimeDelta: ...


class Vallado:
    def __new__(cls, initial_state: State, max_iter: int | None = None): ...

    @overload
    def propagate(self, time: Time) -> State: ...

    @overload
    def propagate(self, time: list[Time]) -> Trajectory: ...

    def propagate(self, time: Time | list[Time]) -> State | Trajectory: ...


class GroundLocation:
    def __new__(
            cls,
            planet: Planet,
            longitude: float,
            latitude: float,
            altitude: float,
    ): ...

    def longitude(self) -> float: ...

    def latitude(self) -> float: ...

    def altitude(self) -> float: ...

    def observables(self) -> Observables: ...

    def rotation_to_topocentric(self) -> np.ndarray: ...


class GroundPropagator:
    def __new__(cls, location: GroundLocation, provider: UT1Provider): ...

    @overload
    def propagate(self, time: Time) -> State: ...

    @overload
    def propagate(self, time: list[Time]) -> Trajectory: ...

    def propagate(self, time: Time | list[Time]) -> State | Trajectory: ...


class SGP4:
    def __new__(cls, tle: str): ...

    def time(self) -> Time: ...

    @overload
    def propagate(self, time: Time) -> State: ...

    @overload
    def propagate(self, time: list[Time]) -> Trajectory: ...

    def propagate(self, time: Time | list[Time]) -> State | Trajectory: ...


class Observables:
    def __new__(cls, azimuth: float, elevation: float, range: float, range_rate: float): ...

    def azimuth(self) -> float: ...

    def elevation(self) -> float: ...

    def range(self) -> float: ...

    def range_rate(self) -> float: ...


class Time:
    def __new__(
            cls,
            scale: Scale,
            year: int,
            month: int,
            day: int,
            hour: int = 0,
            minute: int = 0,
            seconds: float = 0.0,
    ): ...

    @classmethod
    def from_julian_date(cls, scale: Scale, jd: float, epoch: str = "jd") -> Self: ...

    @classmethod
    def from_two_part_julian_date(
            cls, scale: Scale, jd1: float, jd2: float
    ) -> Self: ...

    @classmethod
    def from_day_of_year(
            cls,
            scale: Scale,
            year: int,
            doy: int,
            hour: int = 0,
            minute: int = 0,
            seconds: float = 0.0,
    ) -> Self: ...

    @classmethod
    def from_iso(cls, iso: str, scale: Scale | None = None) -> Self: ...

    @classmethod
    def from_seconds(cls, scale: Scale, seconds: int, subsecond: float) -> Self: ...

    def seconds(self) -> int: ...

    def subsecond(self) -> float: ...

    def __str__(self) -> str: ...

    def __repr__(self) -> str: ...

    def __add__(self, other: TimeDelta) -> Self: ...

    @overload
    def __sub__(self, other: TimeDelta) -> Self: ...

    @overload
    def __sub__(self, other: Time) -> TimeDelta: ...

    def __eq__(self, other: object) -> bool: ...

    def __lt__(self, other: object) -> bool: ...

    def __le__(self, other: object) -> bool: ...

    def isclose(
            self, other: Time, rel_tol: float = 1e-8, abs_tol: float = 1e-14
    ) -> bool: ...

    def julian_date(
            self,
            epoch: Epoch = "jd",
            unit: Unit = "days",
    ) -> float: ...

    def two_part_julian_date(self) -> tuple[float, float]: ...

    def scale(self) -> Scale: ...

    def year(self) -> int: ...

    def month(self) -> int: ...

    def day(self) -> int: ...

    def day_of_year(self) -> int: ...

    def hour(self) -> int: ...

    def minute(self) -> int: ...

    def second(self) -> int: ...

    def millisecond(self) -> int: ...

    def microsecond(self) -> int: ...

    def nanosecond(self) -> int: ...

    def picosecond(self) -> int: ...

    def femtosecond(self) -> int: ...

    def decimal_seconds(self) -> float: ...

    def to_tai(self, provider: UT1Provider | None = None) -> Self: ...

    def to_tcb(self, provider: UT1Provider | None = None) -> Self: ...

    def to_tcg(self, provider: UT1Provider | None = None) -> Self: ...

    def to_tdb(self, provider: UT1Provider | None = None) -> Self: ...

    def to_tt(self, provider: UT1Provider | None = None) -> Self: ...

    def to_ut1(self, provider: UT1Provider | None = None) -> Self: ...

    def to_utc(self, provider: UT1Provider | None = None) -> UTC: ...


class TimeDelta:
    def __new__(cls, seconds: float): ...

    def __repr__(self) -> str: ...

    def __str__(self) -> str: ...

    def __float__(self) -> float: ...

    def __neg__(self) -> Self: ...

    def __add__(self, other: Self) -> Self: ...

    def __sub__(self, other: Self) -> Self: ...

    def seconds(self) -> int: ...

    def subsecond(self) -> float: ...

    @classmethod
    def from_seconds(cls, seconds: int) -> Self: ...

    @classmethod
    def from_minutes(cls, minutes: float) -> Self: ...

    @classmethod
    def from_hours(cls, hours: float) -> Self: ...

    @classmethod
    def from_days(cls, days: float) -> Self: ...

    @classmethod
    def from_julian_years(cls, years: float) -> Self: ...

    @classmethod
    def from_julian_centuries(cls, centuries: float) -> Self: ...

    def to_decimal_seconds(self) -> float: ...

    @classmethod
    def range(cls, start: int, end: int, step: int | None = None) -> list[Self]: ...


class UTC:
    def __new__(
            cls,
            year: int,
            month: int,
            day: int,
            hour: int = 0,
            minute: int = 0,
            seconds: float = 0.0,
    ): ...

    @classmethod
    def from_iso(cls, iso: str) -> Self: ...

    def __str__(self) -> str: ...

    def __repr__(self) -> str: ...

    def __eq__(self, other: object) -> bool: ...

    def year(self) -> int: ...

    def month(self) -> int: ...

    def day(self) -> int: ...

    def hour(self) -> int: ...

    def minute(self) -> int: ...

    def second(self) -> int: ...

    def millisecond(self) -> int: ...

    def microsecond(self) -> int: ...

    def nanosecond(self) -> int: ...

    def picosecond(self) -> int: ...

    def decimal_seconds(self) -> float: ...

    def to_tai(self) -> Time: ...

    def to_tcb(self) -> Time: ...

    def to_tcg(self) -> Time: ...

    def to_tdb(self) -> Time: ...

    def to_tt(self) -> Time: ...

    def to_ut1(self, provider: UT1Provider) -> Time: ...


class UT1Provider:
    def __new__(cls, path: str): ...


type Origin = Sun | Barycenter | Planet | Satellite | MinorBody


class Sun:
    def __new__(cls): ...

    def id(self) -> int: ...

    def name(self) -> str: ...

    def gravitational_parameter(self) -> float: ...

    def mean_radius(self) -> float: ...

    def polar_radius(self) -> float: ...

    def equatorial_radius(self) -> float: ...


class Barycenter:
    def __new__(cls, name: str): ...

    def id(self) -> int: ...

    def name(self) -> str: ...

    def gravitational_parameter(self) -> float: ...


class Planet:
    def __new__(cls, name: str): ...

    def id(self) -> int: ...

    def name(self) -> str: ...

    def gravitational_parameter(self) -> float: ...

    def mean_radius(self) -> float: ...

    def polar_radius(self) -> float: ...

    def equatorial_radius(self) -> float: ...


class Satellite:
    def __new__(cls, name: str): ...

    def id(self) -> int: ...

    def name(self) -> str: ...

    def gravitational_parameter(self) -> float: ...

    def mean_radius(self) -> float: ...

    def polar_radius(self) -> float: ...

    def subplanetary_radius(self) -> float: ...

    def along_orbit_radius(self) -> float: ...


class MinorBody:
    def __new__(cls, name: str): ...

    def id(self) -> int: ...

    def name(self) -> str: ...

    def gravitational_parameter(self) -> float: ...

    def mean_radius(self) -> float: ...

    def polar_radius(self) -> float: ...

    def subplanetary_radius(self) -> float: ...

    def along_orbit_radius(self) -> float: ...
