-- ============================================================================
-- E2EE Session State Tracking
-- ============================================================================
-- Server-side session state tracking to detect and recover from session
-- mismatches between parties. The server cannot see session keys, only
-- metadata about whether sessions exist.
-- ============================================================================

-- ============================================================================
-- 1. Session State Table
-- ============================================================================

-- Track which users have established sessions with each other
-- This allows detection of asymmetric session states (one party has session, other doesn't)
CREATE TABLE IF NOT EXISTS e2ee_session_state (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Session participants (always stored with user_id < peer_id for consistency)
    user_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
    peer_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,

    -- Session state flags
    user_has_session BOOLEAN NOT NULL DEFAULT FALSE,
    peer_has_session BOOLEAN NOT NULL DEFAULT FALSE,

    -- Session metadata (no actual keys stored!)
    user_session_created_at TIMESTAMPTZ,
    peer_session_created_at TIMESTAMPTZ,

    -- Version tracking for conflict resolution
    user_session_version INTEGER NOT NULL DEFAULT 0,
    peer_session_version INTEGER NOT NULL DEFAULT 0,

    -- Timestamps
    created_at TIMESTAMPTZ DEFAULT NOW(),
    updated_at TIMESTAMPTZ DEFAULT NOW(),

    -- Ensure unique pair (user_id should always be < peer_id)
    CONSTRAINT e2ee_session_state_pair_unique UNIQUE (user_id, peer_id),
    CONSTRAINT e2ee_session_state_ordering CHECK (user_id < peer_id)
);

-- Indexes for efficient lookups
CREATE INDEX IF NOT EXISTS idx_session_state_user ON e2ee_session_state(user_id);
CREATE INDEX IF NOT EXISTS idx_session_state_peer ON e2ee_session_state(peer_id);
CREATE INDEX IF NOT EXISTS idx_session_state_mismatch ON e2ee_session_state(user_has_session, peer_has_session)
    WHERE user_has_session != peer_has_session;

-- ============================================================================
-- 2. RLS Policies
-- ============================================================================

ALTER TABLE e2ee_session_state ENABLE ROW LEVEL SECURITY;

-- Users can see session state for their own sessions
CREATE POLICY session_state_select_own ON e2ee_session_state
    FOR SELECT USING (auth.uid() = user_id OR auth.uid() = peer_id);

-- Users can only insert/update their own side of the session
-- (handled via function to enforce ordering)

-- ============================================================================
-- 3. Helper Functions
-- ============================================================================

-- Function to update session state (handles ordering automatically)
CREATE OR REPLACE FUNCTION update_e2ee_session_state(
    p_user_id UUID,
    p_peer_id UUID,
    p_has_session BOOLEAN
) RETURNS JSONB AS $$
DECLARE
    v_lower_id UUID;
    v_higher_id UUID;
    v_is_user_lower BOOLEAN;
    v_result JSONB;
    v_session_state RECORD;
BEGIN
    -- Determine ordering (user_id < peer_id constraint)
    IF p_user_id < p_peer_id THEN
        v_lower_id := p_user_id;
        v_higher_id := p_peer_id;
        v_is_user_lower := TRUE;
    ELSE
        v_lower_id := p_peer_id;
        v_higher_id := p_user_id;
        v_is_user_lower := FALSE;
    END IF;

    -- Insert or update
    INSERT INTO e2ee_session_state (user_id, peer_id, user_has_session, peer_has_session, user_session_created_at, peer_session_created_at, user_session_version, peer_session_version)
    VALUES (
        v_lower_id,
        v_higher_id,
        CASE WHEN v_is_user_lower THEN p_has_session ELSE FALSE END,
        CASE WHEN NOT v_is_user_lower THEN p_has_session ELSE FALSE END,
        CASE WHEN v_is_user_lower AND p_has_session THEN NOW() ELSE NULL END,
        CASE WHEN NOT v_is_user_lower AND p_has_session THEN NOW() ELSE NULL END,
        CASE WHEN v_is_user_lower THEN 1 ELSE 0 END,
        CASE WHEN NOT v_is_user_lower THEN 1 ELSE 0 END
    )
    ON CONFLICT (user_id, peer_id) DO UPDATE SET
        user_has_session = CASE
            WHEN v_is_user_lower THEN p_has_session
            ELSE e2ee_session_state.user_has_session
        END,
        peer_has_session = CASE
            WHEN NOT v_is_user_lower THEN p_has_session
            ELSE e2ee_session_state.peer_has_session
        END,
        user_session_created_at = CASE
            WHEN v_is_user_lower AND p_has_session AND e2ee_session_state.user_session_created_at IS NULL THEN NOW()
            WHEN v_is_user_lower AND NOT p_has_session THEN NULL
            ELSE e2ee_session_state.user_session_created_at
        END,
        peer_session_created_at = CASE
            WHEN NOT v_is_user_lower AND p_has_session AND e2ee_session_state.peer_session_created_at IS NULL THEN NOW()
            WHEN NOT v_is_user_lower AND NOT p_has_session THEN NULL
            ELSE e2ee_session_state.peer_session_created_at
        END,
        user_session_version = CASE
            WHEN v_is_user_lower THEN e2ee_session_state.user_session_version + 1
            ELSE e2ee_session_state.user_session_version
        END,
        peer_session_version = CASE
            WHEN NOT v_is_user_lower THEN e2ee_session_state.peer_session_version + 1
            ELSE e2ee_session_state.peer_session_version
        END,
        updated_at = NOW()
    RETURNING * INTO v_session_state;

    -- Build result with mismatch detection
    v_result := jsonb_build_object(
        'success', TRUE,
        'user_has_session', CASE WHEN v_is_user_lower THEN v_session_state.user_has_session ELSE v_session_state.peer_has_session END,
        'peer_has_session', CASE WHEN v_is_user_lower THEN v_session_state.peer_has_session ELSE v_session_state.user_has_session END,
        'session_mismatch', v_session_state.user_has_session != v_session_state.peer_has_session,
        'peer_session_version', CASE WHEN v_is_user_lower THEN v_session_state.peer_session_version ELSE v_session_state.user_session_version END
    );

    RETURN v_result;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- Function to get session state between two users
CREATE OR REPLACE FUNCTION get_e2ee_session_state(
    p_user_id UUID,
    p_peer_id UUID
) RETURNS JSONB AS $$
DECLARE
    v_lower_id UUID;
    v_higher_id UUID;
    v_is_user_lower BOOLEAN;
    v_session_state RECORD;
BEGIN
    -- Determine ordering
    IF p_user_id < p_peer_id THEN
        v_lower_id := p_user_id;
        v_higher_id := p_peer_id;
        v_is_user_lower := TRUE;
    ELSE
        v_lower_id := p_peer_id;
        v_higher_id := p_user_id;
        v_is_user_lower := FALSE;
    END IF;

    SELECT * INTO v_session_state
    FROM e2ee_session_state
    WHERE user_id = v_lower_id AND peer_id = v_higher_id;

    IF NOT FOUND THEN
        RETURN jsonb_build_object(
            'exists', FALSE,
            'user_has_session', FALSE,
            'peer_has_session', FALSE,
            'session_mismatch', FALSE
        );
    END IF;

    RETURN jsonb_build_object(
        'exists', TRUE,
        'user_has_session', CASE WHEN v_is_user_lower THEN v_session_state.user_has_session ELSE v_session_state.peer_has_session END,
        'peer_has_session', CASE WHEN v_is_user_lower THEN v_session_state.peer_has_session ELSE v_session_state.user_has_session END,
        'session_mismatch', v_session_state.user_has_session != v_session_state.peer_has_session,
        'user_session_version', CASE WHEN v_is_user_lower THEN v_session_state.user_session_version ELSE v_session_state.peer_session_version END,
        'peer_session_version', CASE WHEN v_is_user_lower THEN v_session_state.peer_session_version ELSE v_session_state.user_session_version END
    );
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- Function to clear session state (when resetting)
CREATE OR REPLACE FUNCTION clear_e2ee_session_state(
    p_user_id UUID,
    p_peer_id UUID
) RETURNS JSONB AS $$
DECLARE
    v_lower_id UUID;
    v_higher_id UUID;
    v_is_user_lower BOOLEAN;
BEGIN
    -- Determine ordering
    IF p_user_id < p_peer_id THEN
        v_lower_id := p_user_id;
        v_higher_id := p_peer_id;
        v_is_user_lower := TRUE;
    ELSE
        v_lower_id := p_peer_id;
        v_higher_id := p_user_id;
        v_is_user_lower := FALSE;
    END IF;

    -- Update only the caller's side of the session
    UPDATE e2ee_session_state SET
        user_has_session = CASE WHEN v_is_user_lower THEN FALSE ELSE user_has_session END,
        peer_has_session = CASE WHEN NOT v_is_user_lower THEN FALSE ELSE peer_has_session END,
        user_session_created_at = CASE WHEN v_is_user_lower THEN NULL ELSE user_session_created_at END,
        peer_session_created_at = CASE WHEN NOT v_is_user_lower THEN NULL ELSE peer_session_created_at END,
        user_session_version = CASE WHEN v_is_user_lower THEN user_session_version + 1 ELSE user_session_version END,
        peer_session_version = CASE WHEN NOT v_is_user_lower THEN peer_session_version + 1 ELSE peer_session_version END,
        updated_at = NOW()
    WHERE user_id = v_lower_id AND peer_id = v_higher_id;

    RETURN jsonb_build_object('success', TRUE);
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- ============================================================================
-- 4. Add decryption_failure event type if not exists
-- ============================================================================

-- Update the check constraint to include session_mismatch event type
ALTER TABLE e2ee_session_events
    DROP CONSTRAINT IF EXISTS e2ee_session_events_event_type_check;

ALTER TABLE e2ee_session_events
    ADD CONSTRAINT e2ee_session_events_event_type_check
    CHECK (event_type IN ('session_reset', 'conversation_cleanup', 'key_refresh', 'decryption_failure', 'session_mismatch', 'session_established'));

-- ============================================================================
-- 5. Realtime for session state changes
-- ============================================================================

ALTER PUBLICATION supabase_realtime ADD TABLE e2ee_session_state;

-- ============================================================================
-- 6. Comments
-- ============================================================================

COMMENT ON TABLE e2ee_session_state IS 'Server-side tracking of E2EE session existence between user pairs. Does NOT store actual keys.';
COMMENT ON COLUMN e2ee_session_state.user_has_session IS 'Whether the user with smaller UUID has an active session';
COMMENT ON COLUMN e2ee_session_state.peer_has_session IS 'Whether the user with larger UUID has an active session';
COMMENT ON COLUMN e2ee_session_state.user_session_version IS 'Incremented each time user updates their session state';
COMMENT ON FUNCTION update_e2ee_session_state IS 'Update session state for a user-peer pair. Handles UUID ordering automatically.';
COMMENT ON FUNCTION get_e2ee_session_state IS 'Get session state between two users. Returns mismatch detection.';
COMMENT ON FUNCTION clear_e2ee_session_state IS 'Clear session state for a user (used during session reset).';
