-- pg_orrery 0.16.0 -> 0.17.0: solar elongation, planet phase, satellite eclipse,
-- observing night quality, lunar libration

-- ============================================================
-- Solar elongation (1)
-- ============================================================

CREATE FUNCTION solar_elongation(int4, timestamptz) RETURNS float8
    AS 'MODULE_PATHNAME', 'solar_elongation'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION solar_elongation(int4, timestamptz) IS
    'Sun-Earth-Planet angle in degrees [0, 180]. How far a planet appears from the Sun. Body IDs 1-8.';

-- ============================================================
-- Planet phase fraction (1)
-- ============================================================

CREATE FUNCTION planet_phase(int4, timestamptz) RETURNS float8
    AS 'MODULE_PATHNAME', 'planet_phase'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION planet_phase(int4, timestamptz) IS
    'Illuminated fraction of a planet disk as seen from Earth [0.0, 1.0]. Body IDs 1-8.';

-- ============================================================
-- Satellite eclipse prediction (4)
-- ============================================================

CREATE FUNCTION satellite_is_eclipsed(tle, timestamptz) RETURNS bool
    AS 'MODULE_PATHNAME', 'satellite_is_eclipsed'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_is_eclipsed(tle, timestamptz) IS
    'True if the satellite is in Earth cylindrical shadow at the given time.';

CREATE FUNCTION satellite_next_eclipse_entry(tle, timestamptz) RETURNS timestamptz
    AS 'MODULE_PATHNAME', 'satellite_next_eclipse_entry'
    LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_next_eclipse_entry(tle, timestamptz) IS
    'Next time the satellite enters Earth shadow (up to 7-day search). NULL if none found.';

CREATE FUNCTION satellite_next_eclipse_exit(tle, timestamptz) RETURNS timestamptz
    AS 'MODULE_PATHNAME', 'satellite_next_eclipse_exit'
    LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_next_eclipse_exit(tle, timestamptz) IS
    'Next time the satellite exits Earth shadow (up to 7-day search). NULL if none found.';

CREATE FUNCTION satellite_eclipse_fraction(tle, timestamptz, timestamptz) RETURNS float8
    AS 'MODULE_PATHNAME', 'satellite_eclipse_fraction'
    LANGUAGE C STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION satellite_eclipse_fraction(tle, timestamptz, timestamptz) IS
    'Fraction of the given time window the satellite spends in eclipse [0.0, 1.0].';

-- ============================================================
-- Observing night quality (1)
-- ============================================================

CREATE FUNCTION observing_night_quality(observer, timestamptz DEFAULT NOW())
RETURNS text AS $$
DECLARE
    astro_dusk  timestamptz;
    astro_dawn  timestamptz;
    dark_hours  float8;
    illum       float8;
    moon_up     bool;
    score       int := 100;
BEGIN
    -- Astronomical darkness window
    astro_dusk := sun_astronomical_dusk($1, $2);
    IF astro_dusk IS NULL THEN
        RETURN 'poor';  -- No astronomical darkness (polar summer)
    END IF;
    astro_dawn := sun_astronomical_dawn($1, astro_dusk);
    IF astro_dawn IS NULL THEN
        RETURN 'poor';
    END IF;

    dark_hours := extract(epoch FROM astro_dawn - astro_dusk) / 3600.0;

    -- Short dark window penalty
    IF dark_hours < 2.0 THEN score := score - 40;
    ELSIF dark_hours < 4.0 THEN score := score - 20;
    ELSIF dark_hours < 6.0 THEN score := score - 10;
    END IF;

    -- Moon illumination penalty
    illum := moon_illumination(astro_dusk);
    IF illum > 0.75 THEN
        -- Check if Moon is above horizon during darkness
        moon_up := topo_elevation(moon_observe($1, astro_dusk)) > 0
                OR topo_elevation(moon_observe($1, astro_dusk + (astro_dawn - astro_dusk) / 2)) > 0;
        IF moon_up THEN
            score := score - (illum * 30)::int;  -- Up to -30 for full moon
        END IF;
    END IF;

    -- Classify
    IF score >= 80 THEN RETURN 'excellent';
    ELSIF score >= 60 THEN RETURN 'good';
    ELSIF score >= 40 THEN RETURN 'fair';
    ELSE RETURN 'poor';
    END IF;
END;
$$ LANGUAGE plpgsql STABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION observing_night_quality(observer, timestamptz) IS
    'Composite observing quality assessment: excellent/good/fair/poor based on darkness duration and Moon interference.';

-- ============================================================
-- Lunar libration (5)
-- ============================================================

CREATE FUNCTION moon_libration_longitude(timestamptz) RETURNS float8
    AS 'MODULE_PATHNAME', 'moon_libration_longitude'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration_longitude(timestamptz) IS
    'Optical libration in longitude (degrees, typically [-8, +8]). Meeus Ch. 53.';

CREATE FUNCTION moon_libration_latitude(timestamptz) RETURNS float8
    AS 'MODULE_PATHNAME', 'moon_libration_latitude'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration_latitude(timestamptz) IS
    'Optical libration in latitude (degrees, typically [-7, +7]). Meeus Ch. 53.';

CREATE FUNCTION moon_libration_position_angle(timestamptz) RETURNS float8
    AS 'MODULE_PATHNAME', 'moon_libration_position_angle'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration_position_angle(timestamptz) IS
    'Position angle of the Moon axis (degrees, [0, 360)). Meeus Ch. 53.';

CREATE FUNCTION moon_libration(timestamptz,
    OUT l float8, OUT b float8, OUT p float8) RETURNS record
    AS 'MODULE_PATHNAME', 'moon_libration'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_libration(timestamptz) IS
    'All three libration values: longitude (l), latitude (b), position angle (p) in degrees.';

CREATE FUNCTION moon_subsolar_longitude(timestamptz) RETURNS float8
    AS 'MODULE_PATHNAME', 'moon_subsolar_longitude'
    LANGUAGE C IMMUTABLE STRICT PARALLEL SAFE;
COMMENT ON FUNCTION moon_subsolar_longitude(timestamptz) IS
    'Selenographic longitude of the sub-solar point (degrees, [0, 360)). Determines the lunar terminator position.';
