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

-- Merged from: 22_cross_connection, 23_transactions
-- Tests: df.signal() and df.cancel() from a different backend connection (dblink),
--        transaction semantics — committed txn executes, rolled-back txn does not
-- Runs as postgres throughout (uses dblink with postgres user)

-- === Test: 22_cross_connection ===

DROP TABLE IF EXISTS cross_conn_log;
CREATE TABLE cross_conn_log (
    id SERIAL PRIMARY KEY,
    msg TEXT,
    data JSONB,
    created_at TIMESTAMPTZ DEFAULT now()
);

-- Connection string for dblink (same database, different connection)
CREATE TEMP TABLE _dblink_conn AS 
SELECT format('host=localhost dbname=postgres port=%s user=postgres', current_setting('port')) AS connstr;

-- Test 1: Signal from Different Connection
CREATE TEMP TABLE _test_cross_signal (instance_id TEXT);

INSERT INTO _test_cross_signal SELECT df.start(
    'SELECT ''started''' |=> 'msg'
    ~> (df.wait_for_signal('external_trigger') |=> 'sig')
    ~> 'INSERT INTO cross_conn_log (msg, data) 
        VALUES (''signal_received'', $sig::jsonb)',
    'cross-conn-signal'
);

-- Wait for workflow to reach signal wait state
SELECT pg_sleep(2);

-- Verify workflow is waiting (not completed)
DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_signal;
    SELECT s INTO status FROM df.status(inst_id) s;
    
    IF lower(status) = 'completed' THEN
        RAISE EXCEPTION 'TEST FAILED: workflow should be waiting, not completed';
    END IF;
    
    RAISE NOTICE 'Workflow % is waiting (status: %)', inst_id, status;
END $$;

-- Signal from DIFFERENT connection using dblink
DO $$
DECLARE
    inst_id TEXT;
    connstr TEXT;
    result TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_signal;
    SELECT c.connstr INTO connstr FROM _dblink_conn c;
    
    SELECT * INTO result FROM dblink(
        connstr,
        format('SELECT df.signal(%L, ''external_trigger'', ''{"source": "other_connection", "value": 123}'')', inst_id)
    ) AS t(result TEXT);
    
    RAISE NOTICE 'Signal sent from other connection: %', result;
END $$;

-- Wait for completion and verify
DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_signal;

    SELECT df.await_instance(inst_id, 10) INTO status;

    IF status != 'completed' THEN
        RAISE EXCEPTION 'TEST FAILED: cross-connection signal status = %', status;
    END IF;
    
    IF NOT EXISTS (
        SELECT 1 FROM cross_conn_log 
        WHERE msg = 'signal_received'
        AND (data->'data'->>'source') = 'other_connection'
        AND (data->'data'->>'value')::int = 123
    ) THEN
        RAISE EXCEPTION 'TEST FAILED: signal data from other connection not received correctly';
    END IF;
    
    RAISE NOTICE 'TEST PASSED: cross_connection_signal';
END $$;

DROP TABLE _test_cross_signal;
DELETE FROM cross_conn_log;

-- Test 2: Cancel from Different Connection
CREATE TEMP TABLE _test_cross_cancel (instance_id TEXT);

INSERT INTO _test_cross_cancel SELECT df.start(
    @> (
        'INSERT INTO cross_conn_log (msg) VALUES (''loop_iteration'')'
        ~> df.sleep(1)
    ),
    'cross-conn-cancel'
);

-- Wait for at least one loop iteration to be committed
DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
    iteration_count INT;
    attempts INT := 0;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_cancel;
    LOOP
        SELECT COUNT(*) INTO iteration_count FROM cross_conn_log WHERE msg = 'loop_iteration';
        EXIT WHEN iteration_count >= 1 OR attempts > 100;
        PERFORM pg_sleep(0.1);
        attempts := attempts + 1;
    END LOOP;

    SELECT s INTO status FROM df.status(inst_id) s;
    
    IF iteration_count < 1 THEN
        RAISE EXCEPTION 'TEST FAILED: loop should have at least 1 iteration after 10s, got %', iteration_count;
    END IF;
    
    RAISE NOTICE 'Workflow % is running (status: %, iterations: %)', inst_id, status, iteration_count;
END $$;

-- Cancel from DIFFERENT connection using dblink
DO $$
DECLARE
    inst_id TEXT;
    connstr TEXT;
    result TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_cancel;
    SELECT c.connstr INTO connstr FROM _dblink_conn c;
    
    SELECT * INTO result FROM dblink(
        connstr,
        format('SELECT df.cancel(%L, ''Canceled from external system'')', inst_id)
    ) AS t(result TEXT);
    
    RAISE NOTICE 'Cancel sent from other connection: %', result;
END $$;

DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_cancel;

    SELECT df.await_instance(inst_id, 10) INTO status;

    IF lower(status) != 'cancelled' THEN
        RAISE EXCEPTION 'TEST FAILED: cross-connection cancel status = % (expected cancelled)', status;
    END IF;
    
    RAISE NOTICE 'TEST PASSED: cross_connection_cancel';
END $$;

DROP TABLE _test_cross_cancel;

-- Test 3: Monitor from Different Connection
CREATE TEMP TABLE _test_cross_monitor (instance_id TEXT);

INSERT INTO _test_cross_monitor SELECT df.start(
    'SELECT 1' ~> df.sleep(1) ~> 'SELECT 2',
    'cross-conn-monitor'
);

DO $$
DECLARE
    inst_id TEXT;
    connstr TEXT;
    remote_status TEXT;
    local_status TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_monitor;
    SELECT c.connstr INTO connstr FROM _dblink_conn c;
    
    SELECT * INTO remote_status FROM dblink(
        connstr,
        format('SELECT df.status(%L)', inst_id)
    ) AS t(status TEXT);
    
    SELECT s INTO local_status FROM df.status(inst_id) s;
    
    RAISE NOTICE 'Status from other connection: %, from this connection: %', remote_status, local_status;
    
    IF remote_status IS NULL OR local_status IS NULL THEN
        RAISE EXCEPTION 'TEST FAILED: could not get status from both connections';
    END IF;
    
    RAISE NOTICE 'TEST PASSED: cross_connection_monitor';
END $$;

DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM _test_cross_monitor;
    SELECT df.await_instance(inst_id, 10) INTO status;
END $$;

DROP TABLE _test_cross_monitor;
DROP TABLE cross_conn_log;

-- === Test: 23_transactions ===

DROP TABLE IF EXISTS txn_test_log;
CREATE TABLE txn_test_log (
    id SERIAL PRIMARY KEY,
    msg TEXT,
    instance_id TEXT,
    created_at TIMESTAMPTZ DEFAULT now()
);

-- Test 1: DF Started Within DO Block (Same Transaction) - Should Work via Retry
DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
    attempts INT := 0;
BEGIN
    RAISE NOTICE 'Test 1: DF started within same transaction (tests retry logic)';
    
    inst_id := df.start(
        'INSERT INTO txn_test_log (msg, instance_id) VALUES (''same_txn_df_ran'', ''{sys_instance_id}'')',
        'txn-test-same-txn'
    );
    
    RAISE NOTICE 'Started DF: %. Worker will retry until this txn commits...', inst_id;
    
    PERFORM pg_sleep(0.5);
    
    RAISE NOTICE 'Transaction about to commit after 500ms delay...';
    
    INSERT INTO txn_test_log (msg, instance_id) VALUES ('test1_instance_id', inst_id);
END $$;

DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
BEGIN
    SELECT instance_id INTO inst_id FROM txn_test_log WHERE msg = 'test1_instance_id';
    RAISE NOTICE 'Test 1: Waiting for DF: %', inst_id;
    
    SELECT df.await_instance(inst_id, 10) INTO status;
    
    IF status != 'completed' THEN
        RAISE EXCEPTION 'TEST FAILED: same-txn DF status = % (retry logic should have waited for commit)', status;
    END IF;
    
    IF NOT EXISTS (SELECT 1 FROM txn_test_log WHERE msg = 'same_txn_df_ran') THEN
        RAISE EXCEPTION 'TEST FAILED: same-txn DF did not execute';
    END IF;
    
    RAISE NOTICE 'TEST PASSED: same_transaction_with_retry';
END $$;

DELETE FROM txn_test_log;

-- Test 2: Rolled Back Transaction - DF Should NOT Execute
DO $$
DECLARE
    inst_id TEXT;
    status TEXT;
    attempts INT := 0;
BEGIN
    RAISE NOTICE 'Test 2: Rolled back transaction (via savepoint)';
    
    BEGIN
        inst_id := df.start(
            'INSERT INTO txn_test_log (msg) VALUES (''rollback_df_should_not_run'')',
            'txn-test-rollback'
        );
        
        RAISE NOTICE 'Started DF that will be rolled back: %', inst_id;
        
        INSERT INTO txn_test_log (msg, instance_id) VALUES ('rollback_test_id', inst_id);
        
        RAISE EXCEPTION 'Simulated rollback';
        
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'Transaction rolled back (exception caught)';
    END;
    
    RAISE NOTICE 'After rollback, checking if DF data persisted...';
END $$;

DO $$
DECLARE
    rollback_id TEXT;
    node_count INT;
    instance_count INT;
BEGIN
    SELECT instance_id INTO rollback_id FROM txn_test_log WHERE msg = 'rollback_test_id';
    
    IF rollback_id IS NOT NULL THEN
        RAISE EXCEPTION 'TEST FAILED: rollback_test_id should have been rolled back but found: %', rollback_id;
    END IF;
    
    SELECT COUNT(*) INTO instance_count FROM df.instances WHERE label = 'txn-test-rollback';
    
    IF instance_count > 0 THEN
        RAISE EXCEPTION 'TEST FAILED: rolled back DF should not exist in df.instances';
    END IF;
    
    RAISE NOTICE 'TEST PASSED: rollback_transaction_no_instance';
END $$;

SELECT pg_sleep(2);

DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM txn_test_log WHERE msg = 'rollback_df_should_not_run') THEN
        RAISE EXCEPTION 'TEST FAILED: rolled back DF should not have executed';
    END IF;
    
    RAISE NOTICE 'TEST PASSED: rollback_transaction_no_execution';
END $$;

-- Test 3: Explicit Transaction with Rollback (using dblink)
DO $$
DECLARE
    connstr TEXT := format('host=localhost dbname=postgres port=%s user=postgres', current_setting('port'));
    result TEXT;
    instance_count INT;
BEGIN
    RAISE NOTICE 'Test 3: Explicit transaction rollback via separate connection';
    
    BEGIN
        SELECT * INTO result FROM dblink(
            connstr,
            $dblink$
                BEGIN;
                SELECT df.start(
                    'INSERT INTO txn_test_log (msg) VALUES (''explicit_rollback_should_not_run'')',
                    'txn-test-explicit-rollback'
                );
                ROLLBACK;
            $dblink$
        ) AS t(result TEXT);
    EXCEPTION WHEN OTHERS THEN
        RAISE NOTICE 'dblink completed (exception: %)', SQLERRM;
    END;
    
    PERFORM pg_sleep(2);
    
    SELECT COUNT(*) INTO instance_count FROM df.instances WHERE label = 'txn-test-explicit-rollback';
    
    IF instance_count > 0 THEN
        RAISE EXCEPTION 'TEST FAILED: explicitly rolled back DF should not exist';
    END IF;
    
    IF EXISTS (SELECT 1 FROM txn_test_log WHERE msg = 'explicit_rollback_should_not_run') THEN
        RAISE EXCEPTION 'TEST FAILED: explicitly rolled back DF should not have executed';
    END IF;
    
    RAISE NOTICE 'TEST PASSED: explicit_rollback_transaction';
END $$;

DROP TABLE txn_test_log;

SELECT 'ALL CROSS-CONNECTION AND TRANSACTION TESTS PASSED' AS result;
