-- WAL checkpoint table for write-ahead logging and recovery
CREATE TABLE IF NOT EXISTS wal_checkpoints (
    id UUID PRIMARY KEY,
    sequence_number BIGINT NOT NULL UNIQUE,
    checkpoint_timestamp TIMESTAMPTZ NOT NULL,
    database_state_hash TEXT NOT NULL,
    entries_count BIGINT NOT NULL DEFAULT 0,
    file_path TEXT NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- Index for efficient checkpoint queries
CREATE INDEX IF NOT EXISTS idx_wal_checkpoints_sequence ON wal_checkpoints(sequence_number DESC);
CREATE INDEX IF NOT EXISTS idx_wal_checkpoints_timestamp ON wal_checkpoints(checkpoint_timestamp DESC);

-- Add sequence number to audit_logs for WAL ordering
ALTER TABLE audit_logs ADD COLUMN IF NOT EXISTS sequence_number BIGINT;

-- Create sequence for audit log entries if not exists
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_sequences WHERE sequencename = 'audit_logs_sequence_seq') THEN
        CREATE SEQUENCE audit_logs_sequence_seq START 1;
    END IF;
END $$;

-- Set default for sequence number
ALTER TABLE audit_logs ALTER COLUMN sequence_number SET DEFAULT nextval('audit_logs_sequence_seq');

-- Update existing audit_logs entries to have sequence numbers
DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM audit_logs WHERE sequence_number IS NULL LIMIT 1) THEN
        WITH ordered_logs AS (
            SELECT id, ROW_NUMBER() OVER (ORDER BY created_at ASC) as seq_num
            FROM audit_logs 
            WHERE sequence_number IS NULL
        )
        UPDATE audit_logs 
        SET sequence_number = ordered_logs.seq_num 
        FROM ordered_logs 
        WHERE audit_logs.id = ordered_logs.id;
        
        -- Update sequence to continue from max value
        PERFORM setval('audit_logs_sequence_seq', (SELECT COALESCE(MAX(sequence_number), 0) FROM audit_logs));
    END IF;
END $$;

-- Make sequence_number NOT NULL after populating existing data
ALTER TABLE audit_logs ALTER COLUMN sequence_number SET NOT NULL;

-- Create unique index on sequence number
CREATE UNIQUE INDEX IF NOT EXISTS idx_audit_logs_sequence_unique ON audit_logs(sequence_number);

-- Additional indexes for WAL operations
CREATE INDEX IF NOT EXISTS idx_audit_logs_session_sequence ON audit_logs(session_id, sequence_number);

-- Function to get next WAL sequence number (atomic)
CREATE OR REPLACE FUNCTION get_next_wal_sequence()
RETURNS BIGINT 
LANGUAGE plpgsql
AS $$
DECLARE
    next_seq BIGINT;
BEGIN
    SELECT nextval('audit_logs_sequence_seq') INTO next_seq;
    RETURN next_seq;
END;
$$;

-- Function to create WAL checkpoint
CREATE OR REPLACE FUNCTION create_wal_checkpoint(
    p_checkpoint_id UUID,
    p_database_state_hash TEXT,
    p_file_path TEXT
)
RETURNS TABLE(
    checkpoint_id UUID,
    sequence_number BIGINT,
    checkpoint_timestamp TIMESTAMPTZ,
    entries_count BIGINT
)
LANGUAGE plpgsql
AS $$
DECLARE
    current_sequence BIGINT;
    current_count BIGINT;
    checkpoint_time TIMESTAMPTZ := NOW();
BEGIN
    -- Get current WAL position
    SELECT COALESCE(MAX(audit_logs.sequence_number), 0) INTO current_sequence FROM audit_logs;
    SELECT COUNT(*) INTO current_count FROM audit_logs;
    
    -- Insert checkpoint
    INSERT INTO wal_checkpoints (
        id, sequence_number, checkpoint_timestamp, database_state_hash, 
        entries_count, file_path, created_at
    ) VALUES (
        p_checkpoint_id, current_sequence, checkpoint_time, 
        p_database_state_hash, current_count, p_file_path, checkpoint_time
    );
    
    -- Return checkpoint info
    RETURN QUERY SELECT 
        p_checkpoint_id, 
        current_sequence, 
        checkpoint_time, 
        current_count;
END;
$$;

-- Function to verify WAL integrity
CREATE OR REPLACE FUNCTION verify_wal_integrity(
    p_start_sequence BIGINT,
    p_end_sequence BIGINT
)
RETURNS TABLE(
    sequence_number BIGINT,
    is_valid BOOLEAN,
    expected_checksum TEXT,
    actual_checksum TEXT
)
LANGUAGE plpgsql
AS $$
BEGIN
    RETURN QUERY 
    SELECT 
        al.sequence_number,
        TRUE as is_valid, -- Simplified integrity check
        '' as expected_checksum,
        '' as actual_checksum
    FROM audit_logs al
    WHERE al.sequence_number >= p_start_sequence 
    AND al.sequence_number <= p_end_sequence
    ORDER BY al.sequence_number;
END;
$$;

-- Function to cleanup old WAL entries before checkpoint
CREATE OR REPLACE FUNCTION cleanup_wal_before_checkpoint(p_checkpoint_id UUID)
RETURNS BIGINT
LANGUAGE plpgsql
AS $$
DECLARE
    checkpoint_sequence BIGINT;
    deleted_count BIGINT;
BEGIN
    -- Get checkpoint sequence number
    SELECT wc.sequence_number INTO checkpoint_sequence 
    FROM wal_checkpoints wc 
    WHERE wc.id = p_checkpoint_id;
    
    IF checkpoint_sequence IS NULL THEN
        RAISE EXCEPTION 'Checkpoint not found: %', p_checkpoint_id;
    END IF;
    
    -- Delete audit logs before checkpoint, keeping some safety margin
    DELETE FROM audit_logs 
    WHERE sequence_number < (checkpoint_sequence - 1000);  -- Keep 1000 entries as safety margin
    
    GET DIAGNOSTICS deleted_count = ROW_COUNT;
    
    RETURN deleted_count;
END;
$$;

-- WAL statistics view for monitoring
CREATE OR REPLACE VIEW wal_statistics AS
SELECT 
    COUNT(*) as total_entries,
    MIN(timestamp) as oldest_entry_timestamp,
    MAX(timestamp) as newest_entry_timestamp,
    MIN(sequence_number) as min_sequence,
    MAX(sequence_number) as max_sequence,
    pg_size_pretty(pg_total_relation_size('audit_logs')) as table_size,
    (SELECT COUNT(*) FROM wal_checkpoints) as checkpoint_count,
    (SELECT MAX(sequence_number) FROM wal_checkpoints) as last_checkpoint_sequence,
    CASE 
        WHEN MAX(timestamp) > MIN(timestamp) 
        THEN COUNT(*)::FLOAT / EXTRACT(epoch FROM (MAX(timestamp) - MIN(timestamp))) 
        ELSE 0 
    END as avg_entries_per_second
FROM audit_logs;

-- Grant permissions for trading engine user
DO $$
BEGIN
    IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'foxhunt_trader') THEN
        GRANT SELECT, INSERT ON wal_checkpoints TO foxhunt_trader;
        GRANT SELECT, INSERT, UPDATE ON audit_logs TO foxhunt_trader;
        GRANT USAGE ON SEQUENCE audit_logs_sequence_seq TO foxhunt_trader;
        GRANT SELECT ON wal_statistics TO foxhunt_trader;
        GRANT EXECUTE ON FUNCTION get_next_wal_sequence() TO foxhunt_trader;
        GRANT EXECUTE ON FUNCTION create_wal_checkpoint(UUID, TEXT, TEXT) TO foxhunt_trader;
        GRANT EXECUTE ON FUNCTION verify_wal_integrity(BIGINT, BIGINT) TO foxhunt_trader;
        GRANT EXECUTE ON FUNCTION cleanup_wal_before_checkpoint(UUID) TO foxhunt_trader;
    END IF;
END $$;