-- ============================================================
-- Migration 013: Distributed Entry Locking
-- Date: 2026-02-18
-- Purpose: Add deterministic distributed locking + lifecycle guard for ENTRY flows.
-- ============================================================

-- Determine a stable 64-bit key per profile+symbol pair.
CREATE OR REPLACE FUNCTION fn_entry_lock_key(p_profile uuid, p_symbol text)
RETURNS bigint AS $$
DECLARE
    profile_hash bigint;
    symbol_hash bigint;
BEGIN
    profile_hash := abs(hashtext(coalesce(p_profile::text, ''))::bigint) % 2147483647;
    symbol_hash := abs(hashtext(coalesce(lower(p_symbol), ''))::bigint) % 4294967295;
    RETURN ((profile_hash << 32) + symbol_hash) % 9223372036854775807;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

-- Try to obtain the advisory lock without blocking.
CREATE OR REPLACE FUNCTION fn_try_acquire_entry_lock(p_profile uuid, p_symbol text)
RETURNS boolean AS $$
BEGIN
    RETURN pg_try_advisory_lock(fn_entry_lock_key(p_profile, p_symbol));
END;
$$ LANGUAGE plpgsql;

-- Release the advisory lock.
CREATE OR REPLACE FUNCTION fn_release_entry_lock(p_profile uuid, p_symbol text)
RETURNS boolean AS $$
BEGIN
    RETURN pg_advisory_unlock(fn_entry_lock_key(p_profile, p_symbol));
END;
$$ LANGUAGE plpgsql;

-- Check for an existing ENTRY/OPEN lifecycle for the same profile+symbol.
CREATE OR REPLACE FUNCTION fn_is_entry_in_progress(p_profile uuid, p_symbol text)
RETURNS boolean AS $$
BEGIN
    RETURN EXISTS (
        SELECT 1
        FROM trade_lifecycle tl
        JOIN positions p ON p.profile_id = tl.profile_id AND p.trade_id = tl.trade_id
        WHERE tl.profile_id = p_profile
          AND lower(p.symbol) = lower(coalesce(p_symbol, ''))
          AND tl.current_stage IN ('ENTRY', 'OPEN')
    );
END;
$$ LANGUAGE plpgsql STABLE;
