-- ================================================================================================
-- Migration 003: Comprehensive Audit System Schema
-- Complete audit trail for regulatory compliance with immutable logging
-- Includes ML events, system events, and complete change tracking
-- ================================================================================================

-- ================================================================================================
-- AUDIT EVENT TYPES AND ENUMS
-- Comprehensive classification for all auditable events
-- ================================================================================================

CREATE TYPE audit_event_type AS ENUM (
    -- Trading audit events
    'order_created',
    'order_modified',
    'order_cancelled',
    'order_executed',
    'trade_settled',
    'position_updated',

    -- Risk management audit events
    'risk_limit_breached',
    'risk_limit_updated',
    'emergency_action_taken',
    'compliance_check_failed',
    'model_validation_failed',

    -- ML and Algorithm audit events
    'model_prediction',
    'model_training_started',
    'model_training_completed',
    'model_deployed',
    'model_rollback',
    'signal_generated',
    'algorithm_decision',
    'backtest_executed',

    -- System audit events
    'system_startup',
    'system_shutdown',
    'service_restart',
    'configuration_changed',
    'user_login',
    'user_logout',
    'permission_granted',
    'permission_revoked',
    'data_export',
    'data_import',

    -- Security audit events
    'authentication_success',
    'authentication_failure',
    'authorization_failure',
    'suspicious_activity',
    'security_breach_detected',
    'encryption_key_rotated',

    -- Market data audit events
    'market_data_received',
    'market_data_gap_detected',
    'circuit_breaker_triggered',
    'trading_halt_detected',

    -- Compliance audit events
    'regulatory_report_generated',
    'audit_trail_accessed',
    'data_retention_policy_applied',
    'compliance_validation_completed'
);

CREATE TYPE audit_severity AS ENUM (
    'trace',       -- Detailed debugging information
    'debug',       -- General debugging information
    'info',        -- General information
    'notice',      -- Normal but significant condition
    'warning',     -- Warning conditions
    'error',       -- Error conditions
    'critical',    -- Critical conditions requiring immediate attention
    'alert',       -- Action must be taken immediately
    'emergency'    -- System is unusable
);

CREATE TYPE system_component AS ENUM (
    'trading_engine',
    'risk_management',
    'market_data',
    'order_management',
    'portfolio_management',
    'ml_engine',
    'execution_engine',
    'compliance_engine',
    'authentication',
    'configuration',
    'database',
    'api_gateway',
    'user_interface',
    'reporting',
    'monitoring',
    'backup_system'
);

-- ================================================================================================
-- COMPREHENSIVE AUDIT LOG TABLE
-- Immutable audit trail for all system activities
-- ================================================================================================
CREATE TABLE audit_log (
    -- Primary identifiers
    id UUID DEFAULT uuid_generate_v4(),
    audit_id BIGSERIAL NOT NULL, -- Sequential audit ID for ordering
    correlation_id UUID, -- Links related audit entries

    -- Timing with nanosecond precision
    event_timestamp ns_timestamp NOT NULL, -- When the event actually occurred
    recorded_timestamp ns_timestamp NOT NULL, -- When the audit entry was created
    processing_timestamp ns_timestamp NOT NULL, -- When the system processed the event

    -- Event classification
    event_type audit_event_type NOT NULL,
    severity audit_severity NOT NULL DEFAULT 'info',
    component system_component NOT NULL,

    -- Event context and scope
    entity_type VARCHAR(100), -- Type of entity affected (order, position, user, etc.)
    entity_id UUID, -- ID of the affected entity
    parent_entity_type VARCHAR(100), -- Parent entity type
    parent_entity_id UUID, -- Parent entity ID

    -- User and session context
    user_id VARCHAR(64),
    session_id UUID,
    impersonated_user_id VARCHAR(64), -- If acting on behalf of another user
    client_id VARCHAR(100), -- Application or API client

    -- Request context
    request_id UUID, -- Original request that triggered this event
    trace_id UUID, -- Distributed tracing ID
    span_id UUID, -- Distributed tracing span ID

    -- Network and system context
    source_ip INET,
    user_agent TEXT,
    source_host VARCHAR(255),
    source_port INTEGER,

    -- Event details
    action VARCHAR(100) NOT NULL, -- Specific action taken
    resource VARCHAR(200), -- Resource or endpoint accessed
    method VARCHAR(20), -- HTTP method or operation type

    -- Data changes (for compliance)
    old_values JSONB, -- Previous state before change
    new_values JSONB, -- New state after change
    affected_fields TEXT[], -- List of fields that were modified

    -- Event payload and metadata
    event_data JSONB NOT NULL, -- Complete event details
    tags JSONB, -- Searchable tags and labels
    metadata JSONB, -- Additional metadata

    -- System and performance context
    node_id VARCHAR(50) NOT NULL, -- Which system node recorded this
    process_id INTEGER NOT NULL, -- OS process ID
    thread_id INTEGER, -- Thread ID
    execution_time_ns BIGINT, -- How long the operation took (nanoseconds)
    memory_usage_bytes BIGINT, -- Memory usage at time of event
    cpu_usage_percent DECIMAL(5,2), -- CPU usage percentage

    -- Security and integrity
    checksum VARCHAR(64) NOT NULL, -- SHA-256 hash of event content
    digital_signature TEXT, -- Digital signature for non-repudiation
    encryption_key_id VARCHAR(100), -- If event data is encrypted

    -- Compliance and retention
    retention_category VARCHAR(50) DEFAULT 'standard', -- Retention policy category
    retention_until DATE, -- When this record can be archived/deleted
    is_sensitive BOOLEAN DEFAULT FALSE, -- Contains sensitive data
    compliance_flags TEXT[], -- Regulatory compliance flags

    -- Error handling
    is_error BOOLEAN DEFAULT FALSE,
    error_code VARCHAR(50),
    error_message TEXT,
    stack_trace TEXT,

    -- Partition key for performance
    audit_date DATE NOT NULL,

    -- Constraints
    PRIMARY KEY (id, audit_date),
    CONSTRAINT chk_audit_timestamps CHECK (
        recorded_timestamp >= event_timestamp AND
        processing_timestamp >= recorded_timestamp
    ),
    CONSTRAINT chk_execution_time CHECK (execution_time_ns IS NULL OR execution_time_ns >= 0),
    CONSTRAINT chk_cpu_usage CHECK (cpu_usage_percent IS NULL OR (cpu_usage_percent >= 0 AND cpu_usage_percent <= 100))
) PARTITION BY RANGE (audit_date);

-- ================================================================================================
-- ML EVENTS TABLE
-- Specialized tracking for machine learning operations
-- ================================================================================================
CREATE TABLE ml_events (
    -- Primary identifiers
    id UUID DEFAULT uuid_generate_v4(),
    model_id VARCHAR(200) NOT NULL, -- Unique model identifier
    model_version VARCHAR(50) NOT NULL,

    -- Timing
    event_timestamp ns_timestamp NOT NULL,

    -- ML event classification
    event_type VARCHAR(100) NOT NULL, -- prediction, training, validation, deployment, etc.
    event_subtype VARCHAR(100), -- More specific classification

    -- Model context
    model_name VARCHAR(200) NOT NULL,
    model_architecture VARCHAR(100), -- transformer, lstm, cnn, etc.
    framework VARCHAR(50), -- tensorflow, pytorch, etc.
    framework_version VARCHAR(50),

    -- Input/Output data
    input_features JSONB, -- Model input features and values
    predictions JSONB, -- Model predictions/outputs
    confidence_scores JSONB, -- Confidence levels for predictions
    feature_importance JSONB, -- Feature importance scores

    -- Performance metrics
    inference_time_ns BIGINT, -- Time taken for inference
    model_accuracy DECIMAL(8,6), -- Model accuracy if available
    model_loss DECIMAL(12,8), -- Model loss/error
    prediction_confidence DECIMAL(8,6), -- Overall prediction confidence

    -- Training context (for training events)
    training_dataset_id VARCHAR(200),
    training_dataset_size INTEGER,
    training_epochs INTEGER,
    learning_rate DECIMAL(10,8),
    batch_size INTEGER,

    -- Validation and testing
    validation_score DECIMAL(8,6),
    test_score DECIMAL(8,6),
    cross_validation_scores DECIMAL(8,6)[],

    -- Model drift and monitoring
    data_drift_score DECIMAL(8,6), -- How much input data has drifted
    model_drift_score DECIMAL(8,6), -- How much model performance has drifted
    feature_drift_scores JSONB, -- Per-feature drift scores
    anomaly_score DECIMAL(8,6), -- Anomaly detection score

    -- Business context
    symbol VARCHAR(32), -- Trading symbol if applicable
    strategy_id VARCHAR(100),
    account_id VARCHAR(64),
    signal_strength DECIMAL(8,6), -- Trading signal strength

    -- System context
    node_id VARCHAR(50) NOT NULL,
    gpu_device_id INTEGER, -- GPU device used
    memory_usage_mb INTEGER,
    gpu_memory_usage_mb INTEGER,

    -- Model artifacts and references
    model_artifact_path TEXT, -- Path to model file
    checkpoint_id VARCHAR(200), -- Training checkpoint reference
    experiment_id VARCHAR(200), -- ML experiment tracking ID

    -- Additional metadata
    hyperparameters JSONB, -- Model hyperparameters
    environment_info JSONB, -- Runtime environment details
    custom_metrics JSONB, -- Domain-specific metrics

    -- Partition key
    event_date DATE NOT NULL,

    -- Constraints
    PRIMARY KEY (id, event_date)
) PARTITION BY RANGE (event_date);

-- ================================================================================================
-- SYSTEM EVENTS TABLE
-- Specialized tracking for system health and performance
-- ================================================================================================
CREATE TABLE system_events (
    -- Primary identifiers
    id UUID DEFAULT uuid_generate_v4(),
    event_timestamp ns_timestamp NOT NULL,

    -- Event classification
    event_type VARCHAR(100) NOT NULL, -- startup, shutdown, health_check, performance_alert, etc.
    severity audit_severity NOT NULL,
    component system_component NOT NULL,
    service_name VARCHAR(100),

    -- System metrics
    cpu_usage_percent DECIMAL(5,2),
    memory_usage_mb INTEGER,
    memory_total_mb INTEGER,
    disk_usage_gb INTEGER,
    disk_total_gb INTEGER,
    network_rx_bytes BIGINT,
    network_tx_bytes BIGINT,
    load_average_1m DECIMAL(8,4),
    load_average_5m DECIMAL(8,4),
    load_average_15m DECIMAL(8,4),

    -- Performance metrics
    latency_p50_ns BIGINT, -- 50th percentile latency
    latency_p95_ns BIGINT, -- 95th percentile latency
    latency_p99_ns BIGINT, -- 99th percentile latency
    throughput_ops_per_sec DECIMAL(12,2),
    error_rate_percent DECIMAL(5,2),

    -- Application-specific metrics
    active_connections INTEGER,
    pending_requests INTEGER,
    orders_per_second DECIMAL(10,2),
    fills_per_second DECIMAL(10,2),
    market_data_messages_per_second DECIMAL(12,2),

    -- Health check details
    health_status VARCHAR(50), -- healthy, degraded, unhealthy, unknown
    health_checks JSONB, -- Individual health check results
    dependencies_status JSONB, -- Status of external dependencies

    -- Configuration and version info
    application_version VARCHAR(100),
    configuration_version VARCHAR(100),
    database_version VARCHAR(100),

    -- Error and debugging information
    error_details JSONB,
    debug_info JSONB,

    -- System context
    node_id VARCHAR(50) NOT NULL,
    process_id INTEGER NOT NULL,
    container_id VARCHAR(100), -- Docker/Kubernetes container ID
    pod_name VARCHAR(100), -- Kubernetes pod name
    namespace VARCHAR(100), -- Kubernetes namespace

    -- Partition key
    event_date DATE NOT NULL,

    -- Constraints
    PRIMARY KEY (id, event_date)
) PARTITION BY RANGE (event_date);

-- ================================================================================================
-- CHANGE TRACKING TABLE
-- Detailed tracking of all data changes for compliance
-- ================================================================================================
CREATE TABLE change_tracking (
    -- Primary identifiers
    id UUID DEFAULT uuid_generate_v4(),
    change_timestamp ns_timestamp NOT NULL,

    -- Change context
    table_name VARCHAR(100) NOT NULL,
    operation VARCHAR(20) NOT NULL CHECK (operation IN ('INSERT', 'UPDATE', 'DELETE')),
    primary_key_values JSONB NOT NULL, -- Primary key values of affected record

    -- Change details
    changed_columns TEXT[], -- Names of columns that changed
    old_row_data JSONB, -- Complete old row data (for UPDATE/DELETE)
    new_row_data JSONB, -- Complete new row data (for INSERT/UPDATE)
    column_changes JSONB, -- Detailed before/after for each changed column

    -- User and session context
    user_id VARCHAR(64),
    session_id UUID,
    application_name VARCHAR(100),

    -- Transaction context
    transaction_id BIGINT, -- Database transaction ID
    statement_id INTEGER, -- Statement within transaction

    -- System context
    node_id VARCHAR(50) NOT NULL,
    process_id INTEGER NOT NULL,

    -- Audit metadata
    audit_log_id UUID, -- Reference to audit_log entry
    checksum VARCHAR(64) NOT NULL, -- Integrity check

    -- Partition key
    change_date DATE NOT NULL,

    -- Constraints
    PRIMARY KEY (id, change_date)
) PARTITION BY RANGE (change_date);

-- ================================================================================================
-- COMPLIANCE ANNOTATIONS TABLE
-- Additional compliance metadata for audit entries
-- ================================================================================================
CREATE TABLE compliance_annotations (
    -- Primary identifiers
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    audit_log_id UUID NOT NULL, -- FK removed: audit_log has composite PK (id, audit_date)

    -- Compliance framework
    regulation_name VARCHAR(100) NOT NULL, -- MiFID II, GDPR, SOX, etc.
    requirement_section VARCHAR(100), -- Specific section/article
    compliance_category VARCHAR(100), -- trade_reporting, record_keeping, etc.

    -- Annotation details
    annotation_type VARCHAR(50) NOT NULL, -- tag, note, exemption, etc.
    annotation_value TEXT,
    is_required BOOLEAN DEFAULT TRUE,

    -- Validation and review
    validated_by VARCHAR(64),
    validated_at TIMESTAMP WITH TIME ZONE,
    review_status VARCHAR(50), -- pending, approved, rejected
    reviewer_notes TEXT,

    -- Retention and archival
    retention_years INTEGER NOT NULL DEFAULT 7,
    archive_after_years INTEGER DEFAULT 10,

    -- Timing
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- ================================================================================================
-- HIGH-PERFORMANCE INDEXES
-- ================================================================================================

-- Audit log indexes (optimized for compliance queries)
CREATE INDEX idx_audit_log_timestamp ON audit_log USING BTREE (event_timestamp);
CREATE INDEX idx_audit_log_user_timestamp ON audit_log USING BTREE (user_id, event_timestamp) WHERE user_id IS NOT NULL;
CREATE INDEX idx_audit_log_entity ON audit_log USING BTREE (entity_type, entity_id) WHERE entity_type IS NOT NULL AND entity_id IS NOT NULL;
CREATE INDEX idx_audit_log_component_type ON audit_log USING BTREE (component, event_type);
CREATE INDEX idx_audit_log_severity ON audit_log USING BTREE (severity, event_timestamp) WHERE severity IN ('error', 'critical', 'alert', 'emergency');
CREATE INDEX idx_audit_log_session ON audit_log USING HASH (session_id) WHERE session_id IS NOT NULL;
CREATE INDEX idx_audit_log_correlation ON audit_log USING HASH (correlation_id) WHERE correlation_id IS NOT NULL;
CREATE INDEX idx_audit_log_trace ON audit_log USING HASH (trace_id) WHERE trace_id IS NOT NULL;
CREATE INDEX idx_audit_log_sensitive ON audit_log USING BTREE (event_timestamp) WHERE is_sensitive = TRUE;

-- GIN indexes for JSONB columns (flexible querying)
CREATE INDEX idx_audit_log_event_data_gin ON audit_log USING GIN (event_data);
CREATE INDEX idx_audit_log_old_values_gin ON audit_log USING GIN (old_values);
CREATE INDEX idx_audit_log_new_values_gin ON audit_log USING GIN (new_values);
CREATE INDEX idx_audit_log_tags_gin ON audit_log USING GIN (tags);

-- ML events indexes
CREATE INDEX idx_ml_events_timestamp ON ml_events USING BTREE (event_timestamp);
CREATE INDEX idx_ml_events_model ON ml_events USING BTREE (model_name, model_version, event_timestamp);
CREATE INDEX idx_ml_events_symbol ON ml_events USING BTREE (symbol, event_timestamp) WHERE symbol IS NOT NULL;
CREATE INDEX idx_ml_events_strategy ON ml_events USING BTREE (strategy_id, event_timestamp) WHERE strategy_id IS NOT NULL;
CREATE INDEX idx_ml_events_type ON ml_events USING BTREE (event_type, event_timestamp);

-- System events indexes
CREATE INDEX idx_system_events_timestamp ON system_events USING BTREE (event_timestamp);
CREATE INDEX idx_system_events_component ON system_events USING BTREE (component, event_timestamp);
CREATE INDEX idx_system_events_severity ON system_events USING BTREE (severity, event_timestamp);
CREATE INDEX idx_system_events_health ON system_events USING BTREE (health_status, event_timestamp) WHERE health_status IS NOT NULL;
CREATE INDEX idx_system_events_node ON system_events USING BTREE (node_id, event_timestamp);

-- Change tracking indexes
CREATE INDEX idx_change_tracking_timestamp ON change_tracking USING BTREE (change_timestamp);
CREATE INDEX idx_change_tracking_table ON change_tracking USING BTREE (table_name, change_timestamp);
CREATE INDEX idx_change_tracking_user ON change_tracking USING BTREE (user_id, change_timestamp) WHERE user_id IS NOT NULL;
CREATE INDEX idx_change_tracking_audit_log ON change_tracking USING HASH (audit_log_id) WHERE audit_log_id IS NOT NULL;

-- Compliance annotations indexes
CREATE INDEX idx_compliance_annotations_audit_log ON compliance_annotations USING HASH (audit_log_id);
CREATE INDEX idx_compliance_annotations_regulation ON compliance_annotations USING BTREE (regulation_name, compliance_category);
CREATE INDEX idx_compliance_annotations_review ON compliance_annotations USING BTREE (review_status, created_at);

-- ================================================================================================
-- AUTOMATIC PARTITIONING
-- ================================================================================================

-- Function to create daily partitions for audit_log
CREATE OR REPLACE FUNCTION create_audit_log_partition(target_date DATE)
RETURNS VOID AS $$
DECLARE
    partition_name TEXT;
    start_date DATE;
    end_date DATE;
BEGIN
    start_date := target_date;
    end_date := target_date + INTERVAL '1 day';
    partition_name := 'audit_log_' || to_char(start_date, 'YYYY_MM_DD');

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.tables
        WHERE table_name = partition_name
    ) THEN
        EXECUTE format('CREATE TABLE %I PARTITION OF audit_log
                       FOR VALUES FROM (%L) TO (%L)',
                       partition_name, start_date, end_date);

        -- Add partition-specific indexes
        EXECUTE format('CREATE INDEX %I ON %I USING BTREE (event_timestamp)',
                       'idx_' || partition_name || '_timestamp', partition_name);
        EXECUTE format('CREATE INDEX %I ON %I USING BTREE (user_id, event_timestamp) WHERE user_id IS NOT NULL',
                       'idx_' || partition_name || '_user_ts', partition_name);
        EXECUTE format('CREATE INDEX %I ON %I USING BTREE (component, event_type)',
                       'idx_' || partition_name || '_comp_type', partition_name);
    END IF;
END;
$$ LANGUAGE plpgsql;

-- Function to create daily partitions for ML events
CREATE OR REPLACE FUNCTION create_ml_events_partition(target_date DATE)
RETURNS VOID AS $$
DECLARE
    partition_name TEXT;
    start_date DATE;
    end_date DATE;
BEGIN
    start_date := target_date;
    end_date := target_date + INTERVAL '1 day';
    partition_name := 'ml_events_' || to_char(start_date, 'YYYY_MM_DD');

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.tables
        WHERE table_name = partition_name
    ) THEN
        EXECUTE format('CREATE TABLE %I PARTITION OF ml_events
                       FOR VALUES FROM (%L) TO (%L)',
                       partition_name, start_date, end_date);

        -- Add partition-specific indexes
        EXECUTE format('CREATE INDEX %I ON %I USING BTREE (event_timestamp)',
                       'idx_' || partition_name || '_timestamp', partition_name);
        EXECUTE format('CREATE INDEX %I ON %I USING BTREE (model_name, event_timestamp)',
                       'idx_' || partition_name || '_model_ts', partition_name);
    END IF;
END;
$$ LANGUAGE plpgsql;

-- Function to create daily partitions for system events
CREATE OR REPLACE FUNCTION create_system_events_partition(target_date DATE)
RETURNS VOID AS $$
DECLARE
    partition_name TEXT;
    start_date DATE;
    end_date DATE;
BEGIN
    start_date := target_date;
    end_date := target_date + INTERVAL '1 day';
    partition_name := 'system_events_' || to_char(start_date, 'YYYY_MM_DD');

    IF NOT EXISTS (
        SELECT 1 FROM information_schema.tables
        WHERE table_name = partition_name
    ) THEN
        EXECUTE format('CREATE TABLE %I PARTITION OF system_events
                       FOR VALUES FROM (%L) TO (%L)',
                       partition_name, start_date, end_date);

        -- Add partition-specific indexes
        EXECUTE format('CREATE INDEX %I ON %I USING BTREE (event_timestamp)',
                       'idx_' || partition_name || '_timestamp', partition_name);
        EXECUTE format('CREATE INDEX %I ON %I USING BTREE (component, event_timestamp)',
                       'idx_' || partition_name || '_comp_ts', partition_name);
    END IF;
END;
$$ LANGUAGE plpgsql;

-- Create initial partitions for all audit tables
DO $$
DECLARE
    i INTEGER;
BEGIN
    -- Create partitions for current and next 30 days
    FOR i IN 0..30 LOOP
        PERFORM create_audit_log_partition(CURRENT_DATE + i);
        PERFORM create_ml_events_partition(CURRENT_DATE + i);
        PERFORM create_system_events_partition(CURRENT_DATE + i);
        -- Reuse trading_events partition function for change_tracking
        PERFORM create_trading_events_partition(CURRENT_DATE + i);
    END LOOP;
END $$;

-- ================================================================================================
-- AUDIT HELPER FUNCTIONS
-- ================================================================================================

-- Function to create audit log entry
CREATE OR REPLACE FUNCTION create_audit_entry(
    p_event_type audit_event_type,
    p_component system_component,
    p_action VARCHAR(100),
    p_entity_type VARCHAR(100) DEFAULT NULL,
    p_entity_id UUID DEFAULT NULL,
    p_user_id VARCHAR(64) DEFAULT NULL,
    p_session_id UUID DEFAULT NULL,
    p_event_data JSONB DEFAULT '{}'::jsonb,
    p_severity audit_severity DEFAULT 'info',
    p_old_values JSONB DEFAULT NULL,
    p_new_values JSONB DEFAULT NULL
) RETURNS UUID AS $$
DECLARE
    audit_entry_id UUID;
    current_timestamp_ns ns_timestamp;
BEGIN
    audit_entry_id := uuid_generate_v4();
    current_timestamp_ns := EXTRACT(EPOCH FROM NOW()) * 1000000000;

    INSERT INTO audit_log (
        id, event_timestamp, recorded_timestamp, processing_timestamp,
        event_type, severity, component, entity_type, entity_id,
        user_id, session_id, action, event_data,
        old_values, new_values, node_id, process_id, checksum
    ) VALUES (
        audit_entry_id,
        current_timestamp_ns,
        current_timestamp_ns,
        current_timestamp_ns,
        p_event_type,
        p_severity,
        p_component,
        p_entity_type,
        p_entity_id,
        p_user_id,
        p_session_id,
        p_action,
        p_event_data,
        p_old_values,
        p_new_values,
        'audit-node-01', -- node_id literal; overridden per-deployment via later migrations
        pg_backend_pid(),
        encode(sha256(audit_entry_id::text::bytea), 'hex')
    );

    RETURN audit_entry_id;
END;
$$ LANGUAGE plpgsql;

-- Function to log ML event
CREATE OR REPLACE FUNCTION log_ml_event(
    p_model_name VARCHAR(200),
    p_model_version VARCHAR(50),
    p_event_type VARCHAR(100),
    p_predictions JSONB DEFAULT NULL,
    p_confidence_scores JSONB DEFAULT NULL,
    p_symbol VARCHAR(32) DEFAULT NULL,
    p_strategy_id VARCHAR(100) DEFAULT NULL,
    p_inference_time_ns BIGINT DEFAULT NULL,
    p_additional_data JSONB DEFAULT '{}'::jsonb
) RETURNS UUID AS $$
DECLARE
    ml_event_id UUID;
    current_timestamp_ns ns_timestamp;
BEGIN
    ml_event_id := uuid_generate_v4();
    current_timestamp_ns := EXTRACT(EPOCH FROM NOW()) * 1000000000;

    INSERT INTO ml_events (
        id, model_id, model_version, event_timestamp,
        event_type, model_name, predictions, confidence_scores,
        symbol, strategy_id, inference_time_ns, node_id,
        custom_metrics
    ) VALUES (
        ml_event_id,
        p_model_name || ':' || p_model_version,
        p_model_version,
        current_timestamp_ns,
        p_event_type,
        p_model_name,
        p_predictions,
        p_confidence_scores,
        p_symbol,
        p_strategy_id,
        p_inference_time_ns,
        'ml-node-01', -- node_id literal; overridden per-deployment via later migrations
        p_additional_data
    );

    RETURN ml_event_id;
END;
$$ LANGUAGE plpgsql;

-- Function to log system event
CREATE OR REPLACE FUNCTION log_system_event(
    p_event_type VARCHAR(100),
    p_component system_component,
    p_severity audit_severity DEFAULT 'info',
    p_service_name VARCHAR(100) DEFAULT NULL,
    p_health_status VARCHAR(50) DEFAULT NULL,
    p_metrics JSONB DEFAULT '{}'::jsonb,
    p_error_details JSONB DEFAULT NULL
) RETURNS UUID AS $$
DECLARE
    system_event_id UUID;
    current_timestamp_ns ns_timestamp;
BEGIN
    system_event_id := uuid_generate_v4();
    current_timestamp_ns := EXTRACT(EPOCH FROM NOW()) * 1000000000;

    INSERT INTO system_events (
        id, event_timestamp, event_type, severity, component,
        service_name, health_status, node_id, process_id,
        error_details, debug_info
    ) VALUES (
        system_event_id,
        current_timestamp_ns,
        p_event_type,
        p_severity,
        p_component,
        p_service_name,
        p_health_status,
        'system-node-01', -- node_id literal; overridden per-deployment via later migrations
        pg_backend_pid(),
        p_error_details,
        p_metrics
    );

    RETURN system_event_id;
END;
$$ LANGUAGE plpgsql;

-- ================================================================================================
-- AUTOMATED CHANGE TRACKING TRIGGERS
-- ================================================================================================

-- Generic function to track changes on any table
CREATE OR REPLACE FUNCTION track_table_changes()
RETURNS TRIGGER AS $$
DECLARE
    change_record_id UUID;
    current_timestamp_ns ns_timestamp;
    old_data JSONB;
    new_data JSONB;
    changed_cols TEXT[];
BEGIN
    change_record_id := uuid_generate_v4();
    current_timestamp_ns := EXTRACT(EPOCH FROM NOW()) * 1000000000;

    -- Convert row data to JSONB
    IF TG_OP = 'DELETE' THEN
        old_data := to_jsonb(OLD);
        new_data := NULL;
    ELSIF TG_OP = 'INSERT' THEN
        old_data := NULL;
        new_data := to_jsonb(NEW);
    ELSE -- UPDATE
        old_data := to_jsonb(OLD);
        new_data := to_jsonb(NEW);

        -- Identify changed columns
        SELECT array_agg(key) INTO changed_cols
        FROM (
            SELECT key
            FROM jsonb_each_text(old_data) o
            FULL OUTER JOIN jsonb_each_text(new_data) n USING (key)
            WHERE o.value IS DISTINCT FROM n.value
        ) t;
    END IF;

    -- Insert change tracking record
    INSERT INTO change_tracking (
        id, change_timestamp, table_name, operation,
        primary_key_values, changed_columns, old_row_data, new_row_data,
        node_id, process_id, checksum
    ) VALUES (
        change_record_id,
        current_timestamp_ns,
        TG_TABLE_NAME,
        TG_OP,
        CASE
            WHEN TG_OP = 'DELETE' THEN jsonb_build_object('id', OLD.id)
            ELSE jsonb_build_object('id', NEW.id)
        END,
        changed_cols,
        old_data,
        new_data,
        'change-tracker-01', -- node_id literal; overridden per-deployment via later migrations
        pg_backend_pid(),
        encode(sha256(change_record_id::text::bytea), 'hex')
    );

    RETURN COALESCE(NEW, OLD);
END;
$$ LANGUAGE plpgsql;

-- Function to automatically update compliance annotations timestamp
CREATE OR REPLACE FUNCTION update_compliance_annotations_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at := NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- ================================================================================================
-- TRIGGER FUNCTIONS FOR GENERATED COLUMNS (converted from GENERATED ALWAYS)
-- ================================================================================================

-- Set audit_date for audit_log
CREATE OR REPLACE FUNCTION set_audit_log_date()
RETURNS TRIGGER AS $$
BEGIN
    NEW.audit_date := DATE(TO_TIMESTAMP(NEW.event_timestamp / 1000000000.0));
    RETURN NEW;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

CREATE TRIGGER tg_set_audit_log_date
    BEFORE INSERT OR UPDATE ON audit_log
    FOR EACH ROW
    EXECUTE FUNCTION set_audit_log_date();

-- Set event_date for ml_events
CREATE OR REPLACE FUNCTION set_ml_event_date()
RETURNS TRIGGER AS $$
BEGIN
    NEW.event_date := DATE(TO_TIMESTAMP(NEW.event_timestamp / 1000000000.0));
    RETURN NEW;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

CREATE TRIGGER tg_set_ml_event_date
    BEFORE INSERT OR UPDATE ON ml_events
    FOR EACH ROW
    EXECUTE FUNCTION set_ml_event_date();

-- Set event_date for system_events
CREATE OR REPLACE FUNCTION set_system_event_date()
RETURNS TRIGGER AS $$
BEGIN
    NEW.event_date := DATE(TO_TIMESTAMP(NEW.event_timestamp / 1000000000.0));
    RETURN NEW;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

CREATE TRIGGER tg_set_system_event_date
    BEFORE INSERT OR UPDATE ON system_events
    FOR EACH ROW
    EXECUTE FUNCTION set_system_event_date();

-- Set change_date for change_tracking
CREATE OR REPLACE FUNCTION set_change_tracking_date()
RETURNS TRIGGER AS $$
BEGIN
    NEW.change_date := DATE(TO_TIMESTAMP(NEW.change_timestamp / 1000000000.0));
    RETURN NEW;
END;
$$ LANGUAGE plpgsql IMMUTABLE;

CREATE TRIGGER tg_set_change_tracking_date
    BEFORE INSERT OR UPDATE ON change_tracking
    FOR EACH ROW
    EXECUTE FUNCTION set_change_tracking_date();

-- ================================================================================================
-- CREATE TRIGGERS FOR CHANGE TRACKING
-- ================================================================================================

-- Enable change tracking on core trading tables
CREATE TRIGGER tg_track_orders_changes
    AFTER INSERT OR UPDATE OR DELETE ON orders
    FOR EACH ROW
    EXECUTE FUNCTION track_table_changes();

CREATE TRIGGER tg_track_fills_changes
    AFTER INSERT OR UPDATE OR DELETE ON fills
    FOR EACH ROW
    EXECUTE FUNCTION track_table_changes();

CREATE TRIGGER tg_track_positions_changes
    AFTER INSERT OR UPDATE OR DELETE ON positions
    FOR EACH ROW
    EXECUTE FUNCTION track_table_changes();

CREATE TRIGGER tg_track_risk_limits_changes
    AFTER INSERT OR UPDATE OR DELETE ON risk_limits
    FOR EACH ROW
    EXECUTE FUNCTION track_table_changes();

-- Compliance annotations timestamp trigger
CREATE TRIGGER tg_update_compliance_annotations_timestamp
    BEFORE UPDATE ON compliance_annotations
    FOR EACH ROW
    EXECUTE FUNCTION update_compliance_annotations_timestamp();

-- ================================================================================================
-- AUDIT SEARCH AND REPORTING FUNCTIONS
-- ================================================================================================

-- Function to search audit log with flexible filters
CREATE OR REPLACE FUNCTION search_audit_log(
    p_start_time TIMESTAMP WITH TIME ZONE DEFAULT NOW() - INTERVAL '24 hours',
    p_end_time TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    p_user_id VARCHAR(64) DEFAULT NULL,
    p_component system_component DEFAULT NULL,
    p_event_type audit_event_type DEFAULT NULL,
    p_severity audit_severity DEFAULT NULL,
    p_entity_type VARCHAR(100) DEFAULT NULL,
    p_entity_id UUID DEFAULT NULL,
    p_search_text TEXT DEFAULT NULL,
    p_limit INTEGER DEFAULT 1000
) RETURNS TABLE (
    id UUID,
    event_timestamp TIMESTAMP WITH TIME ZONE,
    event_type audit_event_type,
    severity audit_severity,
    component system_component,
    user_id VARCHAR(64),
    action VARCHAR(100),
    entity_type VARCHAR(100),
    entity_id UUID,
    description TEXT
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        al.id,
        TO_TIMESTAMP(al.event_timestamp / 1000000000.0),
        al.event_type,
        al.severity,
        al.component,
        al.user_id,
        al.action,
        al.entity_type,
        al.entity_id,
        COALESCE(al.event_data->>'description', al.action) as description
    FROM audit_log al
    WHERE al.event_timestamp >= EXTRACT(EPOCH FROM p_start_time) * 1000000000
      AND al.event_timestamp <= EXTRACT(EPOCH FROM p_end_time) * 1000000000
      AND (p_user_id IS NULL OR al.user_id = p_user_id)
      AND (p_component IS NULL OR al.component = p_component)
      AND (p_event_type IS NULL OR al.event_type = p_event_type)
      AND (p_severity IS NULL OR al.severity = p_severity)
      AND (p_entity_type IS NULL OR al.entity_type = p_entity_type)
      AND (p_entity_id IS NULL OR al.entity_id = p_entity_id)
      AND (p_search_text IS NULL OR
           al.event_data::text ILIKE '%' || p_search_text || '%' OR
           al.action ILIKE '%' || p_search_text || '%')
    ORDER BY al.event_timestamp DESC
    LIMIT p_limit;
END;
$$ LANGUAGE plpgsql;

-- Function to get audit trail for specific entity
CREATE OR REPLACE FUNCTION get_entity_audit_trail(
    p_entity_type VARCHAR(100),
    p_entity_id UUID,
    p_start_time TIMESTAMP WITH TIME ZONE DEFAULT NOW() - INTERVAL '30 days'
) RETURNS TABLE (
    event_timestamp TIMESTAMP WITH TIME ZONE,
    event_type audit_event_type,
    action VARCHAR(100),
    user_id VARCHAR(64),
    old_values JSONB,
    new_values JSONB,
    description TEXT
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        TO_TIMESTAMP(al.event_timestamp / 1000000000.0),
        al.event_type,
        al.action,
        al.user_id,
        al.old_values,
        al.new_values,
        COALESCE(al.event_data->>'description', al.action) as description
    FROM audit_log al
    WHERE al.entity_type = p_entity_type
      AND al.entity_id = p_entity_id
      AND al.event_timestamp >= EXTRACT(EPOCH FROM p_start_time) * 1000000000
    ORDER BY al.event_timestamp ASC;
END;
$$ LANGUAGE plpgsql;

-- ================================================================================================
-- REPORTING VIEWS
-- ================================================================================================

-- Daily audit summary view
CREATE VIEW v_daily_audit_summary AS
SELECT
    DATE(TO_TIMESTAMP(al.event_timestamp / 1000000000.0)) as audit_date,
    al.component,
    al.event_type,
    al.severity,
    COUNT(*) as event_count,
    COUNT(DISTINCT al.user_id) as unique_users,
    COUNT(*) FILTER (WHERE al.is_error = TRUE) as error_count,
    COUNT(*) FILTER (WHERE al.severity IN ('critical', 'alert', 'emergency')) as critical_count
FROM audit_log al
WHERE al.event_timestamp >= EXTRACT(EPOCH FROM (NOW() - INTERVAL '30 days')) * 1000000000
GROUP BY
    DATE(TO_TIMESTAMP(al.event_timestamp / 1000000000.0)),
    al.component,
    al.event_type,
    al.severity
ORDER BY audit_date DESC, event_count DESC;

-- User activity summary view
CREATE VIEW v_user_activity_summary AS
SELECT
    al.user_id,
    DATE(TO_TIMESTAMP(al.event_timestamp / 1000000000.0)) as activity_date,
    COUNT(*) as total_actions,
    COUNT(DISTINCT al.component) as components_accessed,
    COUNT(*) FILTER (WHERE al.severity = 'error') as error_count,
    MIN(TO_TIMESTAMP(al.event_timestamp / 1000000000.0)) as first_activity,
    MAX(TO_TIMESTAMP(al.event_timestamp / 1000000000.0)) as last_activity,
    array_agg(DISTINCT al.event_type ORDER BY al.event_type) as event_types
FROM audit_log al
WHERE al.user_id IS NOT NULL
  AND al.event_timestamp >= EXTRACT(EPOCH FROM (NOW() - INTERVAL '30 days')) * 1000000000
GROUP BY al.user_id, DATE(TO_TIMESTAMP(al.event_timestamp / 1000000000.0))
ORDER BY activity_date DESC, total_actions DESC;

-- System health summary view
CREATE VIEW v_system_health_summary AS
SELECT
    se.component,
    se.health_status,
    COUNT(*) as status_count,
    AVG(se.cpu_usage_percent) as avg_cpu_usage,
    AVG(se.memory_usage_mb) as avg_memory_usage,
    AVG(se.latency_p95_ns) / 1000000.0 as avg_p95_latency_ms,
    MAX(TO_TIMESTAMP(se.event_timestamp / 1000000000.0)) as last_reported
FROM system_events se
WHERE se.event_timestamp >= EXTRACT(EPOCH FROM (NOW() - INTERVAL '4 hours')) * 1000000000
  AND se.health_status IS NOT NULL
GROUP BY se.component, se.health_status
ORDER BY se.component, status_count DESC;

-- ================================================================================================
-- RETENTION AND ARCHIVAL POLICIES
-- ================================================================================================

-- Function to archive old audit data
CREATE OR REPLACE FUNCTION archive_audit_data(retention_years INTEGER DEFAULT 7)
RETURNS INTEGER AS $$
DECLARE
    archive_date DATE;
    archived_count INTEGER := 0;
    partition_name TEXT;
BEGIN
    archive_date := CURRENT_DATE - INTERVAL '1 year' * retention_years;

    -- Archive old partitions (implementation depends on archival strategy)
    -- This is a placeholder for actual archival implementation

    -- Drop partitions older than retention period
    FOR partition_name IN
        SELECT table_name
        FROM information_schema.tables
        WHERE table_name LIKE 'audit_log_%'
          AND table_name < 'audit_log_' || to_char(archive_date, 'YYYY_MM_DD')
    LOOP
        -- Move to archive or drop (implement based on requirements)
        EXECUTE format('DROP TABLE %I', partition_name);
        archived_count := archived_count + 1;
    END LOOP;

    RETURN archived_count;
END;
$$ LANGUAGE plpgsql;

-- ================================================================================================
-- COMMENTS AND DOCUMENTATION
-- ================================================================================================

COMMENT ON TABLE audit_log IS 'Comprehensive immutable audit trail for all system activities. Partitioned by date with 7+ year retention for regulatory compliance.';
COMMENT ON TABLE ml_events IS 'Specialized audit log for machine learning operations including model predictions, training, and deployment events.';
COMMENT ON TABLE system_events IS 'System health and performance event tracking with metrics and health status monitoring.';
COMMENT ON TABLE change_tracking IS 'Detailed change tracking for all data modifications with before/after values for compliance reporting.';
COMMENT ON TABLE compliance_annotations IS 'Additional compliance metadata and annotations for audit entries to support regulatory requirements.';

COMMENT ON FUNCTION create_audit_entry IS 'Helper function to create standardized audit log entries with proper formatting and security.';
COMMENT ON FUNCTION log_ml_event IS 'Helper function to log machine learning events with standardized schema and performance metrics.';
COMMENT ON FUNCTION search_audit_log IS 'Flexible audit log search function supporting various filters for compliance reporting and investigation.';
COMMENT ON FUNCTION get_entity_audit_trail IS 'Get complete audit trail for a specific entity showing all changes and activities over time.';