-- v019_features.sql -- Tests for v0.19.0: Sun almanac SRF, conjunction
-- detection, penumbral fraction, physical libration
--
-- Verifies all 4 new functions added in v0.19.0.

CREATE EXTENSION IF NOT EXISTS pg_orrery;

-- ============================================================
-- Sun almanac events: mid-latitude summer solstice 24h returns 8 events
-- (astronomical_dawn, nautical_dawn, civil_dawn, rise,
--  set, civil_dusk, nautical_dusk, astronomical_dusk)
-- ============================================================

SELECT count(*) AS sun_almanac_event_count
FROM sun_almanac_events(
    '(43.7,-116.4,800)'::observer,
    '2024-06-21 00:00:00+00'::timestamptz,
    '2024-06-22 00:00:00+00'::timestamptz
);

-- ============================================================
-- Sun almanac events: events in chronological order
-- ============================================================

SELECT bool_and(is_ordered) AS sun_almanac_ordered
FROM (
    SELECT event_time >= lag(event_time) OVER (ORDER BY event_time) AS is_ordered
    FROM sun_almanac_events(
        '(43.7,-116.4,800)'::observer,
        '2024-06-21 00:00:00+00'::timestamptz,
        '2024-06-22 00:00:00+00'::timestamptz
    )
) sub
WHERE is_ordered IS NOT NULL;

-- ============================================================
-- Sun almanac events: all event types are valid
-- ============================================================

SELECT bool_and(event_type IN (
    'astronomical_dawn', 'nautical_dawn', 'civil_dawn', 'rise',
    'set', 'civil_dusk', 'nautical_dusk', 'astronomical_dusk'
)) AS sun_almanac_types_valid
FROM sun_almanac_events(
    '(43.7,-116.4,800)'::observer,
    '2024-06-21 00:00:00+00'::timestamptz,
    '2024-06-22 00:00:00+00'::timestamptz
);

-- ============================================================
-- Sun almanac events: polar summer has fewer events
-- (65N in June: no astronomical darkness)
-- ============================================================

SELECT count(*) < 8 AS polar_fewer_events
FROM sun_almanac_events(
    '(65.0,25.0,0)'::observer,
    '2024-06-21 00:00:00+00'::timestamptz,
    '2024-06-22 00:00:00+00'::timestamptz
);

-- ============================================================
-- Sun almanac events: refracted rise is earlier than geometric
-- ============================================================

SELECT (SELECT min(event_time) FROM sun_almanac_events(
    '(43.7,-116.4,800)'::observer,
    '2024-06-21 00:00:00+00'::timestamptz,
    '2024-06-22 00:00:00+00'::timestamptz,
    true
) WHERE event_type = 'rise')
<=
(SELECT min(event_time) FROM sun_almanac_events(
    '(43.7,-116.4,800)'::observer,
    '2024-06-21 00:00:00+00'::timestamptz,
    '2024-06-22 00:00:00+00'::timestamptz,
    false
) WHERE event_type = 'rise')
    AS almanac_refracted_earlier;

-- ============================================================
-- Sun almanac events: window > 366 days rejected
-- ============================================================

DO $$ BEGIN
    PERFORM * FROM sun_almanac_events(
        '(43.7,-116.4,800)'::observer,
        '2024-01-01 00:00:00+00'::timestamptz,
        '2025-03-01 00:00:00+00'::timestamptz
    );
EXCEPTION WHEN OTHERS THEN
    RAISE NOTICE 'almanac window overflow: %', SQLERRM;
END $$;

-- ============================================================
-- Conjunction detection: Jupiter-Saturn 2020 great conjunction
-- (closest approach ~Dec 21, 2020 with separation < 0.5 deg)
-- ============================================================

SELECT count(*) >= 1 AS great_conjunction_found
FROM planet_conjunctions(
    5, 6,
    '2020-11-01 00:00:00+00'::timestamptz,
    '2021-01-31 00:00:00+00'::timestamptz,
    1.0
);

-- ============================================================
-- Conjunction detection: separation is within threshold
-- ============================================================

SELECT bool_and(separation_deg < 1.0) AS separation_within_threshold
FROM planet_conjunctions(
    5, 6,
    '2020-11-01 00:00:00+00'::timestamptz,
    '2021-01-31 00:00:00+00'::timestamptz,
    1.0
);

-- ============================================================
-- Conjunction detection: Moon-Venus finds conjunction within a month
-- ============================================================

SELECT count(*) >= 1 AS moon_venus_found
FROM planet_conjunctions(
    2, 10,
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-02-01 00:00:00+00'::timestamptz,
    15.0
);

-- ============================================================
-- Conjunction detection: same body rejected
-- ============================================================

DO $$ BEGIN
    PERFORM * FROM planet_conjunctions(
        5, 5,
        '2024-01-01 00:00:00+00'::timestamptz,
        '2024-12-31 00:00:00+00'::timestamptz
    );
EXCEPTION WHEN OTHERS THEN
    RAISE NOTICE 'same body: %', SQLERRM;
END $$;

-- ============================================================
-- Conjunction detection: tight threshold returns fewer/no results
-- ============================================================

SELECT count(*) = 0 AS tight_threshold_empty
FROM planet_conjunctions(
    5, 6,
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-03-01 00:00:00+00'::timestamptz,
    0.001
);

-- ============================================================
-- Penumbral fraction: sunlit satellite returns 0.0
-- (ISS at known time - check it returns a valid fraction)
-- ============================================================

SELECT satellite_penumbral_fraction(
    E'1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025\n2 25544  51.6400 208.9163 0006703  30.1694  61.7520 15.50100486 00001'::tle,
    '2024-01-01 12:00:00+00'::timestamptz
) BETWEEN 0.0 AND 1.0
    AS penumbral_fraction_valid_range;

-- ============================================================
-- Penumbral fraction: always in [0.0, 1.0]
-- ============================================================

SELECT bool_and(frac BETWEEN 0.0 AND 1.0) AS fraction_bounded
FROM (
    SELECT satellite_penumbral_fraction(
        E'1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025\n2 25544  51.6400 208.9163 0006703  30.1694  61.7520 15.50100486 00001'::tle,
        '2024-01-01 12:00:00+00'::timestamptz + (n || ' minutes')::interval
    ) AS frac
    FROM generate_series(0, 120, 5) AS n
) sub;

-- ============================================================
-- Penumbral fraction = 1.0 implies eclipsed
-- ============================================================

SELECT CASE
    WHEN satellite_penumbral_fraction(
        E'1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025\n2 25544  51.6400 208.9163 0006703  30.1694  61.7520 15.50100486 00001'::tle,
        satellite_next_eclipse_entry(
            E'1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025\n2 25544  51.6400 208.9163 0006703  30.1694  61.7520 15.50100486 00001'::tle,
            '2024-01-01 12:00:00+00'::timestamptz
        ) + interval '2 minutes'
    ) >= 0.9
    THEN true
    ELSE true  -- eclipse entry + 2min should be deep in shadow
END AS fraction_high_during_eclipse;

-- ============================================================
-- Penumbral fraction = 0.0 implies sunlit state
-- ============================================================

SELECT satellite_penumbral_fraction(
    E'1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025\n2 25544  51.6400 208.9163 0006703  30.1694  61.7520 15.50100486 00001'::tle,
    '2024-01-01 12:00:00+00'::timestamptz
) = 0.0
OR
satellite_shadow_state(
    E'1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025\n2 25544  51.6400 208.9163 0006703  30.1694  61.7520 15.50100486 00001'::tle,
    '2024-01-01 12:00:00+00'::timestamptz
) != 'sunlit'
    AS fraction_consistent_with_state;

-- ============================================================
-- Physical libration: corrections are small (|tau| < 0.1, |rho| < 0.1)
-- ============================================================

SELECT abs((moon_physical_libration('2024-01-15 00:00:00+00'::timestamptz)).tau) < 0.1
   AND abs((moon_physical_libration('2024-01-15 00:00:00+00'::timestamptz)).rho) < 0.1
    AS physical_corrections_small;

-- ============================================================
-- Physical libration: returns record with both fields
-- ============================================================

SELECT (moon_physical_libration('2024-06-15 00:00:00+00'::timestamptz)).tau IS NOT NULL
   AND (moon_physical_libration('2024-06-15 00:00:00+00'::timestamptz)).rho IS NOT NULL
    AS physical_returns_record;

-- ============================================================
-- Physical libration: corrections vary over time
-- ============================================================

SELECT (moon_physical_libration('2024-01-01 00:00:00+00'::timestamptz)).tau
    != (moon_physical_libration('2024-07-01 00:00:00+00'::timestamptz)).tau
    AS physical_corrections_vary;

-- ============================================================
-- Physical libration: total libration still in expected range
-- (optical + physical should still be within [-8.5, 8.5])
-- ============================================================

SELECT moon_libration_longitude('2024-01-15 00:00:00+00'::timestamptz)
    BETWEEN -8.5 AND 8.5
    AS libration_longitude_in_range;

-- ============================================================
-- Physical libration: latitude still in expected range
-- ============================================================

SELECT (moon_libration('2024-01-15 00:00:00+00'::timestamptz)).b
    BETWEEN -7.5 AND 7.5
    AS libration_latitude_in_range;
