-- Copyright (c) Microsoft Corporation.
-- Licensed under the PostgreSQL License.

-- Test: df.nodes composite primary key (instance_id, id) — issue #129
-- Verifies two things:
--   1. Schema contract: df.nodes uses a composite PRIMARY KEY (instance_id, id)
--      instead of a global single-column key, and the legacy
--      nodes_instance_node_key UNIQUE constraint is gone (promoted to the PK).
--   2. Regression: a multi-node workflow still completes end-to-end, every node
--      row is updated to 'completed' under instance_id scoping, and df.result()
--      returns the root result. This exercises the instance_id-scoped node-status
--      updates and df.result() lookup that accompany the composite key.

SET SESSION AUTHORIZATION df_e2e_user;

-- === Part 1: schema contract — composite primary key ===
DO $$
DECLARE
    pk_def                   TEXT;
    legacy_unique            BOOLEAN;
    deferred_reference_count INT;
BEGIN
    SELECT pg_get_constraintdef(c.oid) INTO pk_def
    FROM pg_constraint c
    JOIN pg_class t ON t.oid = c.conrelid
    JOIN pg_namespace ns ON ns.oid = t.relnamespace
    WHERE ns.nspname = 'df' AND t.relname = 'nodes' AND c.contype = 'p';

    IF pk_def IS NULL THEN
        RAISE EXCEPTION 'TEST FAILED: df.nodes has no primary key';
    END IF;

    -- pg_get_constraintdef reports key columns in constraint order.
    IF pk_def NOT LIKE 'PRIMARY KEY (instance_id, id)%' THEN
        RAISE EXCEPTION 'TEST FAILED: expected composite PK (instance_id, id), got: %', pk_def;
    END IF;

    -- The composite UNIQUE was promoted to the PK, so it must no longer exist.
    SELECT EXISTS(
        SELECT 1
        FROM pg_constraint c
        JOIN pg_class t ON t.oid = c.conrelid
        JOIN pg_namespace ns ON ns.oid = t.relnamespace
        WHERE ns.nspname = 'df'
          AND t.relname = 'nodes'
          AND c.conname = 'nodes_instance_node_key'
    ) INTO legacy_unique;

    IF legacy_unique THEN
        RAISE EXCEPTION 'TEST FAILED: legacy nodes_instance_node_key UNIQUE still present';
    END IF;

    SELECT count(*) INTO deferred_reference_count
    FROM pg_constraint c
        JOIN pg_class t ON t.oid = c.conrelid
        JOIN pg_namespace ns ON ns.oid = t.relnamespace
        WHERE ns.nspname = 'df'
            AND (t.relname, c.conname) IN (
                    ('nodes', 'nodes_left_node_same_instance_fkey'),
                    ('nodes', 'nodes_right_node_same_instance_fkey'),
                    ('instances', 'instances_root_node_same_instance_fkey')
            )
      AND c.condeferrable
      AND c.condeferred;

    IF deferred_reference_count != 3 THEN
        RAISE EXCEPTION 'TEST FAILED: expected all three graph-reference foreign keys to be DEFERRABLE INITIALLY DEFERRED, found %', deferred_reference_count;
    END IF;

    RAISE NOTICE 'PASSED: df.nodes composite primary key and deferred graph references';
END $$;

-- === Part 2: regression — multi-node workflow under instance_id scoping ===
CREATE TEMP TABLE _test_state (instance_id TEXT);

INSERT INTO _test_state SELECT df.start(
    'SELECT 21 AS num' |=> 'a'
    ~> 'SELECT ($a::int * 2) AS doubled',
    'test-composite-pk-regression'
);

DO $$
DECLARE
    inst_id        TEXT;
    status         TEXT;
    node_total     INT;
    node_completed INT;
    bad_instance   INT;
    result_text    TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_state;
    RAISE NOTICE 'Testing composite-PK regression: %', inst_id;

    SELECT df.await_instance(inst_id) INTO status;

    IF status != 'completed' THEN
        RAISE EXCEPTION 'TEST FAILED: status = %', status;
    END IF;

    -- Every node of this instance must have been updated to 'completed'. If the
    -- instance_id-scoped UPDATE in update_node_status were wrong, some node would
    -- linger in 'pending'/'running'.
    SELECT count(*),
           count(*) FILTER (WHERE n.status = 'completed'),
           count(*) FILTER (WHERE n.instance_id IS DISTINCT FROM inst_id)
      INTO node_total, node_completed, bad_instance
    FROM df.nodes n
    WHERE n.instance_id = inst_id;

    IF node_total < 2 THEN
        RAISE EXCEPTION 'TEST FAILED: expected a multi-node graph, got % node(s)', node_total;
    END IF;

    IF node_completed <> node_total THEN
        RAISE EXCEPTION 'TEST FAILED: % of % nodes completed', node_completed, node_total;
    END IF;

    IF bad_instance <> 0 THEN
        RAISE EXCEPTION 'TEST FAILED: % node(s) have a mismatched instance_id', bad_instance;
    END IF;

    -- df.result() is scoped by instance_id; it must return the root node's
    -- result. The root SQL ('SELECT ($a::int * 2) AS doubled') returns a row
    -- set, so df.result wraps it as {"rows": [{"doubled": 42}], "row_count": 1}.
    -- Assert the exact nested field/value rather than a loose substring so a
    -- regression that surfaced a different node's result (or lost scoping)
    -- cannot pass on an incidental '42' appearing somewhere in the payload.
    SELECT df.result(inst_id) INTO result_text;
    IF result_text IS NULL THEN
        RAISE EXCEPTION 'TEST FAILED: df.result() returned NULL';
    END IF;
    IF (result_text::jsonb #>> '{rows,0,doubled}') IS NULL
       OR (result_text::jsonb #>> '{rows,0,doubled}')::int <> 42
       OR (result_text::jsonb ->> 'row_count')::int <> 1 THEN
        RAISE EXCEPTION 'TEST FAILED: expected root result rows[0].doubled = 42 (row_count 1), got %', result_text;
    END IF;

    RAISE NOTICE 'PASSED: composite-PK regression (% nodes completed)', node_total;
END $$;

DROP TABLE _test_state;

RESET SESSION AUTHORIZATION;
SELECT 'TEST PASSED' AS result;
