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]

class ElevationMask:
    def __new__(cls, azimuth: np.ndarray, elevation: np.ndarray): ...
    @classmethod
    def fixed(cls, min_elevation: float) -> Self: ...

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,
    mask: ElevationMask,
    sc: Trajectory,
    provider: UT1Provider,
): ...

class Origin:
    def __new__(cls, origin: str | int): ...
    def id(self) -> int: ...
    def name(self) -> str: ...

class Frame:
    def __new__(cls, abbreviation: str): ...
    def name(self) -> str: ...
    def abbreviation(self) -> str: ...

class SPK:
    def __new__(cls, path): ...

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) -> Self: ...
    def to_origin(self, target: Origin, ephemeris: SPK) -> Self: ...
    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: ...
    def to_frame(self, frame: Frame) -> Self: ...
    def to_origin(self, target: Origin, ephemeris: SPK) -> Self: ...

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,
        origin: Origin,
        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): ...
