-- Test SP-GiST orbital trie index and &? visibility cone operator
SET client_min_messages = warning;
CREATE EXTENSION IF NOT EXISTS pg_orrery;
RESET client_min_messages;

-- ============================================================
-- Test table with mixed orbital regimes
-- ============================================================
CREATE TABLE test_spgist (
    id serial,
    name text,
    tle tle
);

-- ISS (LEO, ~400km, 51.64 deg)
INSERT INTO test_spgist (name, tle) VALUES ('ISS',
    '1 25544U 98067A   24001.50000000  .00016717  00000-0  10270-3 0  9025
2 25544  51.6400 208.9163 0006703  30.1694  61.7520 15.50100486 00001');

-- Hubble (LEO, ~540km, 28.47 deg)
INSERT INTO test_spgist (name, tle) VALUES ('Hubble',
    '1 20580U 90037B   24001.50000000  .00000790  00000+0  39573-4 0  9992
2 20580  28.4705  61.4398 0002797 317.3115  42.7577 15.09395228 00008');

-- GPS IIR-M (MEO, ~20200km, 55.44 deg)
INSERT INTO test_spgist (name, tle) VALUES ('GPS-IIR',
    '1 28874U 05038A   24001.50000000  .00000012  00000+0  00000+0 0  9993
2 28874  55.4408 300.3467 0117034  51.6543 309.5420  2.00557079 00006');

-- Equatorial-LEO (same altitude as ISS, 5 deg inclination)
INSERT INTO test_spgist (name, tle) VALUES ('Equatorial-LEO',
    '1 99901U 24999A   24001.50000000  .00016717  00000-0  10270-3 0  9990
2 99901   5.0000 208.9163 0006703  30.1694  61.7520 15.50100486 00001');

-- SSO-800 (Sun-synchronous, ~800km, 98.7 deg)
INSERT INTO test_spgist (name, tle) VALUES ('SSO-800',
    '1 99902U 24999B   24001.50000000  .00000100  00000+0  50000-4 0  9991
2 99902  98.7000 120.0000 0001000  90.0000 270.0000 14.19553000 00001');

-- GEO-SAT (Geostationary, ~35786km, 0.04 deg)
INSERT INTO test_spgist (name, tle) VALUES ('GEO-SAT',
    '1 99903U 24999C   24001.50000000  .00000000  00000+0  00000+0 0  9992
2 99903   0.0400 270.0000 0003000   0.0000 180.0000  1.00273791 00001');


-- ============================================================
-- Test 1: Operator standalone — ISS from Eagle Idaho (2h window)
-- Eagle Idaho: 43.6977N 116.3535W, 760m elevation
-- ISS passes altitude and inclination checks, but RAAN filter
-- rejects it — the orbital plane isn't overhead during this
-- specific 2-hour window (correct physics, see Test 5 for 24h).
-- ============================================================
SELECT name,
    ROW(
        observer('43.6977N 116.3535W 760m'),
        '2024-01-01 00:00:00+00'::timestamptz,
        '2024-01-01 02:00:00+00'::timestamptz,
        10.0
    )::observer_window &? tle AS visible
FROM test_spgist
WHERE name = 'ISS';


-- ============================================================
-- Test 2: Equatorial-LEO NOT visible from Eagle Idaho
-- 5 deg inc + ~12 deg footprint = 17 deg < 43.7 deg latitude
-- ============================================================
SELECT name,
    ROW(
        observer('43.6977N 116.3535W 760m'),
        '2024-01-01 00:00:00+00'::timestamptz,
        '2024-01-01 02:00:00+00'::timestamptz,
        10.0
    )::observer_window &? tle AS visible
FROM test_spgist
WHERE name = 'Equatorial-LEO';


-- ============================================================
-- Test 3: Create SP-GiST index, verify index scan with positive
-- results.  Equatorial observer at 0E — SSO-800 RAAN (120 deg)
-- aligns with LST near 0E at this epoch, so it passes.
-- ============================================================
CREATE INDEX test_spgist_idx ON test_spgist USING spgist (tle tle_spgist_ops);

SET enable_seqscan = off;

SELECT name
FROM test_spgist
WHERE ROW(
    observer('0.0N 0.0E 0m'),
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-01-01 02:00:00+00'::timestamptz,
    10.0
)::observer_window &? tle
ORDER BY name;

RESET enable_seqscan;


-- ============================================================
-- Test 4: Seqscan vs index scan consistency — same query must
-- return identical results regardless of scan method.
-- ============================================================
SET enable_indexscan = off;
SET enable_bitmapscan = off;

SELECT name
FROM test_spgist
WHERE ROW(
    observer('0.0N 0.0E 0m'),
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-01-01 02:00:00+00'::timestamptz,
    10.0
)::observer_window &? tle
ORDER BY name;

RESET enable_indexscan;
RESET enable_bitmapscan;


-- ============================================================
-- Test 5: 24-hour window — RAAN filter bypassed (full Earth
-- rotation).  Only ISS and SSO-800 pass inclination from Eagle
-- Idaho (43.7 deg).  Hubble (28.5+14.8=43.3 deg) barely fails.
-- GPS-IIR and GEO-SAT filtered by altitude.
-- ============================================================
SELECT name
FROM test_spgist
WHERE ROW(
    observer('43.6977N 116.3535W 760m'),
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-01-02 00:00:00+00'::timestamptz,
    10.0
)::observer_window &? tle
ORDER BY name;


-- ============================================================
-- Test 6: High min_el (45 deg) changes footprint — wider
-- footprint lets more inclinations through.  Same 24h window.
-- ============================================================
SELECT name
FROM test_spgist
WHERE ROW(
    observer('43.6977N 116.3535W 760m'),
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-01-02 00:00:00+00'::timestamptz,
    45.0
)::observer_window &? tle
ORDER BY name;


-- ============================================================
-- Test 7: GiST coexistence — both index types on same table
-- ============================================================
CREATE INDEX test_gist_idx ON test_spgist USING gist (tle);

-- GiST overlap query still works
SELECT a.name AS sat_a, b.name AS sat_b, a.tle && b.tle AS overlaps
FROM test_spgist a, test_spgist b
WHERE a.name = 'ISS' AND b.name = 'Hubble';

-- SP-GiST query still works alongside GiST
SET enable_seqscan = off;
SELECT name
FROM test_spgist
WHERE ROW(
    observer('43.6977N 116.3535W 760m'),
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-01-02 00:00:00+00'::timestamptz,
    10.0
)::observer_window &? tle
ORDER BY name;
RESET enable_seqscan;


-- ============================================================
-- Test 8: NULL TLE handling — NULLs should be excluded
-- ============================================================
INSERT INTO test_spgist (name, tle) VALUES ('NULL-SAT', NULL);

SELECT name
FROM test_spgist
WHERE ROW(
    observer('43.6977N 116.3535W 760m'),
    '2024-01-01 00:00:00+00'::timestamptz,
    '2024-01-02 00:00:00+00'::timestamptz,
    10.0
)::observer_window &? tle
ORDER BY name;


-- ============================================================
-- Cleanup
-- ============================================================
DROP TABLE test_spgist;
