-- v0.11.0 feature tests: make_orbital_elements constructors + moon equatorial

-- ============================================================
-- Test 1: make_orbital_elements() — radians input
-- Round-trip: construct from radians, read back via accessors
-- ============================================================
SELECT 'make_oe_rad' AS test,
       round(oe_epoch(oe)::numeric, 1) AS epoch,
       round(oe_perihelion(oe)::numeric, 6) AS q_au,
       round(oe_eccentricity(oe)::numeric, 6) AS ecc,
       round(oe_inclination(oe)::numeric, 4) AS inc_deg,
       round(oe_arg_perihelion(oe)::numeric, 4) AS omega_deg,
       round(oe_raan(oe)::numeric, 4) AS node_deg,
       round(oe_h_mag(oe)::numeric, 1) AS h_mag
FROM (SELECT make_orbital_elements(
          2460400.5,    -- epoch JD
          0.587100,     -- q AU
          0.967277,     -- e
          radians(162.2269),  -- inc
          radians(111.8657),  -- omega
          radians(58.1455),   -- Omega
          2460450.123,  -- tp JD
          5.5,          -- H
          4.0           -- G
      ) AS oe) sub;

-- ============================================================
-- Test 2: make_orbital_elements_deg() — degree input
-- Same elements but angles in degrees; should produce identical output
-- ============================================================
SELECT 'make_oe_deg' AS test,
       round(oe_epoch(oe)::numeric, 1) AS epoch,
       round(oe_perihelion(oe)::numeric, 6) AS q_au,
       round(oe_eccentricity(oe)::numeric, 6) AS ecc,
       round(oe_inclination(oe)::numeric, 4) AS inc_deg,
       round(oe_arg_perihelion(oe)::numeric, 4) AS omega_deg,
       round(oe_raan(oe)::numeric, 4) AS node_deg,
       round(oe_h_mag(oe)::numeric, 1) AS h_mag
FROM (SELECT make_orbital_elements_deg(
          2460400.5,    -- epoch JD
          0.587100,     -- q AU
          0.967277,     -- e
          162.2269,     -- inc degrees
          111.8657,     -- omega degrees
          58.1455,      -- Omega degrees
          2460450.123,  -- tp JD
          5.5,          -- H
          4.0           -- G
      ) AS oe) sub;

-- ============================================================
-- Test 3: constructors produce identical results to text I/O
-- The text format uses degrees, so make_orbital_elements_deg should match
-- ============================================================
SELECT 'oe_roundtrip' AS test,
       make_orbital_elements_deg(
           2460400.5, 0.587100, 0.967277,
           162.2269, 111.8657, 58.1455,
           2460450.123, 5.5, 4.0
       )::text
       =
       '(2460400.500000,0.5871000000,0.9672770000,162.226900,111.865700,58.145500,2460450.123000,5.50,4.00)'::orbital_elements::text
       AS matches;

-- ============================================================
-- Test 4: make_orbital_elements() validation — negative q
-- ============================================================
DO $$
BEGIN
    PERFORM make_orbital_elements(2460400.5, -0.1, 0.5, 0, 0, 0, 2460400.5, 0, 0);
    RAISE EXCEPTION 'should have failed';
EXCEPTION WHEN numeric_value_out_of_range THEN
    RAISE NOTICE 'make_oe_neg_q: correctly rejected';
END;
$$;

-- ============================================================
-- Test 5: make_orbital_elements() validation — negative eccentricity
-- ============================================================
DO $$
BEGIN
    PERFORM make_orbital_elements(2460400.5, 1.0, -0.1, 0, 0, 0, 2460400.5, 0, 0);
    RAISE EXCEPTION 'should have failed';
EXCEPTION WHEN numeric_value_out_of_range THEN
    RAISE NOTICE 'make_oe_neg_e: correctly rejected';
END;
$$;

-- ============================================================
-- Test 6: make_orbital_elements used in small_body_equatorial()
-- Verify the constructor output works in the observation pipeline
-- ============================================================
SELECT 'oe_pipeline' AS test,
       round(eq_ra(eq)::numeric, 2) AS ra_hours,
       round(eq_dec(eq)::numeric, 2) AS dec_deg,
       eq_ra(eq) BETWEEN 0 AND 24 AS ra_valid,
       eq_dec(eq) BETWEEN -90 AND 90 AS dec_valid
FROM (SELECT small_body_equatorial(
          make_orbital_elements_deg(
              2460400.5, 0.587100, 0.967277,
              162.2269, 111.8657, 58.1455,
              2460450.123, 5.5, 4.0
          ),
          '2024-06-15 12:00:00+00'::timestamptz
      ) AS eq) sub;

-- ============================================================
-- Test 7: galilean_equatorial — all 4 moons
-- Io, Europa, Ganymede, Callisto should all return valid RA/Dec
-- near Jupiter's position
-- ============================================================
SELECT 'galilean_eq' AS test,
       moon_id,
       round(eq_ra(eq)::numeric, 4) AS ra_hours,
       round(eq_dec(eq)::numeric, 4) AS dec_deg,
       eq_ra(eq) BETWEEN 0 AND 24 AS ra_valid,
       eq_dec(eq) BETWEEN -90 AND 90 AS dec_valid
FROM generate_series(0, 3) AS moon_id,
     LATERAL galilean_equatorial(moon_id, '2024-06-15 12:00:00+00'::timestamptz) AS eq
ORDER BY moon_id;

-- ============================================================
-- Test 8: galilean moons should be near Jupiter
-- All 4 Galilean moons within 0.5 degrees of Jupiter
-- ============================================================
SELECT 'galilean_near_jupiter' AS test,
       moon_id,
       round((galilean_equatorial(moon_id, '2024-06-15 12:00:00+00') <->
              planet_equatorial_apparent(5, '2024-06-15 12:00:00+00'))::numeric, 4)
           AS sep_deg,
       (galilean_equatorial(moon_id, '2024-06-15 12:00:00+00') <->
        planet_equatorial_apparent(5, '2024-06-15 12:00:00+00')) < 0.5
           AS within_half_deg
FROM generate_series(0, 3) AS moon_id
ORDER BY moon_id;

-- ============================================================
-- Test 9: saturn_moon_equatorial — Titan (id=5)
-- Should be near Saturn, within ~0.5 degrees
-- ============================================================
SELECT 'saturn_titan_eq' AS test,
       round(eq_ra(eq)::numeric, 4) AS ra_hours,
       round(eq_dec(eq)::numeric, 4) AS dec_deg,
       round((eq <-> planet_equatorial_apparent(6, '2024-06-15 12:00:00+00'))::numeric, 4) AS sep_from_saturn,
       (eq <-> planet_equatorial_apparent(6, '2024-06-15 12:00:00+00')) < 0.5 AS near_saturn
FROM saturn_moon_equatorial(5, '2024-06-15 12:00:00+00'::timestamptz) AS eq;

-- ============================================================
-- Test 10: uranus_moon_equatorial — Titania (id=3)
-- ============================================================
SELECT 'uranus_titania_eq' AS test,
       round(eq_ra(eq)::numeric, 4) AS ra_hours,
       round(eq_dec(eq)::numeric, 4) AS dec_deg,
       eq_ra(eq) BETWEEN 0 AND 24 AS ra_valid,
       eq_dec(eq) BETWEEN -90 AND 90 AS dec_valid
FROM uranus_moon_equatorial(3, '2024-06-15 12:00:00+00'::timestamptz) AS eq;

-- ============================================================
-- Test 11: mars_moon_equatorial — Phobos and Deimos
-- Both should be near Mars, within ~0.02 degrees (very close moons)
-- ============================================================
SELECT 'mars_moons_eq' AS test,
       moon_id,
       round(eq_ra(eq)::numeric, 4) AS ra_hours,
       round(eq_dec(eq)::numeric, 4) AS dec_deg,
       round((eq <-> planet_equatorial_apparent(4, '2024-06-15 12:00:00+00'))::numeric, 4) AS sep_from_mars
FROM generate_series(0, 1) AS moon_id,
     LATERAL mars_moon_equatorial(moon_id, '2024-06-15 12:00:00+00'::timestamptz) AS eq
ORDER BY moon_id;

-- ============================================================
-- Test 12: galilean_equatorial error — invalid body_id
-- ============================================================
DO $$
BEGIN
    PERFORM galilean_equatorial(5, '2024-06-15 12:00:00+00');
    RAISE EXCEPTION 'should have failed';
EXCEPTION WHEN numeric_value_out_of_range THEN
    RAISE NOTICE 'galilean_eq_invalid: correctly rejected';
END;
$$;
