-- ============================================================================
-- OrbiQuant IA - Esquema AUDIT
-- ============================================================================
-- Archivo: 07_audit_schema.sql
-- Descripción: Logs de auditoría, eventos del sistema y seguridad
-- Fecha: 2025-12-05
-- ============================================================================

SET search_path TO audit;

-- ============================================================================
-- TABLA: audit_logs
-- Descripción: Log general de acciones en el sistema
-- ============================================================================
CREATE TABLE IF NOT EXISTS audit_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

    -- Tabla afectada
    table_name VARCHAR(100) NOT NULL,
    record_id TEXT,

    -- Acción
    action VARCHAR(20) NOT NULL, -- 'INSERT', 'UPDATE', 'DELETE'

    -- Datos
    old_data JSONB,
    new_data JSONB,

    -- Usuario
    user_id UUID,
    user_email VARCHAR(255),

    -- Contexto
    ip_address INET,
    user_agent TEXT,
    session_id UUID,

    -- Timestamp
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Índices para búsqueda eficiente
CREATE INDEX idx_audit_logs_table ON audit_logs(table_name);
CREATE INDEX idx_audit_logs_record ON audit_logs(table_name, record_id);
CREATE INDEX idx_audit_logs_user ON audit_logs(user_id);
CREATE INDEX idx_audit_logs_action ON audit_logs(action);
CREATE INDEX idx_audit_logs_created ON audit_logs(created_at DESC);

-- Particionamiento por mes (para producción)
-- CREATE TABLE audit_logs_2025_01 PARTITION OF audit_logs
--     FOR VALUES FROM ('2025-01-01') TO ('2025-02-01');

-- ============================================================================
-- TABLA: security_events
-- Descripción: Eventos de seguridad
-- ============================================================================
CREATE TYPE security_event_type AS ENUM (
    'login_success',
    'login_failed',
    'logout',
    'password_changed',
    'password_reset_requested',
    'password_reset_completed',
    '2fa_enabled',
    '2fa_disabled',
    '2fa_failed',
    'account_locked',
    'account_unlocked',
    'suspicious_activity',
    'api_key_created',
    'api_key_revoked',
    'permission_denied',
    'rate_limit_exceeded'
);

CREATE TYPE security_severity AS ENUM ('info', 'warning', 'error', 'critical');

CREATE TABLE IF NOT EXISTS security_events (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

    -- Usuario
    user_id UUID REFERENCES auth.users(id),
    user_email VARCHAR(255),

    -- Evento
    event_type security_event_type NOT NULL,
    severity security_severity DEFAULT 'info',
    description TEXT,

    -- Contexto
    ip_address INET,
    user_agent TEXT,
    location JSONB, -- {country, city, lat, lon}

    -- Datos adicionales
    metadata JSONB,

    -- Estado
    acknowledged BOOLEAN DEFAULT FALSE,
    acknowledged_by UUID REFERENCES auth.users(id),
    acknowledged_at TIMESTAMPTZ,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_security_events_user ON security_events(user_id);
CREATE INDEX idx_security_events_type ON security_events(event_type);
CREATE INDEX idx_security_events_severity ON security_events(severity);
CREATE INDEX idx_security_events_created ON security_events(created_at DESC);
CREATE INDEX idx_security_events_unack ON security_events(acknowledged) WHERE acknowledged = FALSE;

-- ============================================================================
-- TABLA: system_events
-- Descripción: Eventos del sistema (no de usuario)
-- ============================================================================
CREATE TYPE system_event_type AS ENUM (
    'service_started',
    'service_stopped',
    'service_error',
    'database_backup',
    'database_restore',
    'deployment',
    'config_changed',
    'scheduled_job_started',
    'scheduled_job_completed',
    'scheduled_job_failed',
    'integration_connected',
    'integration_disconnected',
    'integration_error',
    'alert_triggered',
    'maintenance_started',
    'maintenance_completed'
);

CREATE TABLE IF NOT EXISTS system_events (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

    -- Evento
    event_type system_event_type NOT NULL,
    service_name VARCHAR(100),
    description TEXT,

    -- Severidad
    severity security_severity DEFAULT 'info',

    -- Detalles
    details JSONB,
    error_message TEXT,
    stack_trace TEXT,

    -- Metadata
    hostname VARCHAR(255),
    environment VARCHAR(50), -- 'development', 'staging', 'production'

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_system_events_type ON system_events(event_type);
CREATE INDEX idx_system_events_service ON system_events(service_name);
CREATE INDEX idx_system_events_severity ON system_events(severity);
CREATE INDEX idx_system_events_created ON system_events(created_at DESC);

-- ============================================================================
-- TABLA: trading_audit
-- Descripción: Auditoría específica de operaciones de trading
-- ============================================================================
CREATE TABLE IF NOT EXISTS trading_audit (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

    -- Referencias
    bot_id UUID,
    signal_id UUID,
    position_id UUID,
    account_id UUID,
    user_id UUID,

    -- Evento
    event_type VARCHAR(50) NOT NULL, -- 'signal_generated', 'order_placed', 'order_filled', etc.
    description TEXT,

    -- Datos de la operación
    symbol VARCHAR(20),
    direction VARCHAR(10),
    lot_size DECIMAL(10,4),
    price DECIMAL(20,8),

    -- Precios
    entry_price DECIMAL(20,8),
    stop_loss DECIMAL(20,8),
    take_profit DECIMAL(20,8),

    -- Resultado
    pnl DECIMAL(15,2),
    outcome VARCHAR(20),

    -- Contexto del modelo
    model_id UUID,
    confidence DECIMAL(5,4),
    amd_phase VARCHAR(20),

    -- Metadata
    metadata JSONB,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_trading_audit_bot ON trading_audit(bot_id);
CREATE INDEX idx_trading_audit_signal ON trading_audit(signal_id);
CREATE INDEX idx_trading_audit_position ON trading_audit(position_id);
CREATE INDEX idx_trading_audit_account ON trading_audit(account_id);
CREATE INDEX idx_trading_audit_event ON trading_audit(event_type);
CREATE INDEX idx_trading_audit_symbol ON trading_audit(symbol);
CREATE INDEX idx_trading_audit_created ON trading_audit(created_at DESC);

-- ============================================================================
-- TABLA: api_request_logs
-- Descripción: Logs de requests a la API
-- ============================================================================
CREATE TABLE IF NOT EXISTS api_request_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

    -- Usuario
    user_id UUID,
    api_key_id UUID,

    -- Request
    method VARCHAR(10) NOT NULL,
    path VARCHAR(500) NOT NULL,
    query_params JSONB,
    headers JSONB,
    body_size INT,

    -- Response
    status_code INT,
    response_time_ms INT,
    response_size INT,

    -- Contexto
    ip_address INET,
    user_agent TEXT,

    -- Error (si aplica)
    error_message TEXT,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Índices para análisis
CREATE INDEX idx_api_logs_user ON api_request_logs(user_id);
CREATE INDEX idx_api_logs_path ON api_request_logs(path);
CREATE INDEX idx_api_logs_status ON api_request_logs(status_code);
CREATE INDEX idx_api_logs_created ON api_request_logs(created_at DESC);

-- ============================================================================
-- TABLA: data_access_logs
-- Descripción: Log de acceso a datos sensibles
-- ============================================================================
CREATE TABLE IF NOT EXISTS data_access_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

    -- Usuario que accedió
    user_id UUID NOT NULL REFERENCES auth.users(id),

    -- Dato accedido
    resource_type VARCHAR(100) NOT NULL, -- 'user_profile', 'kyc_document', 'wallet_balance', etc.
    resource_id UUID,
    resource_owner_id UUID, -- Usuario dueño del dato

    -- Acción
    action VARCHAR(50) NOT NULL, -- 'view', 'export', 'modify'

    -- Contexto
    reason TEXT, -- Justificación del acceso
    ip_address INET,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_data_access_user ON data_access_logs(user_id);
CREATE INDEX idx_data_access_resource ON data_access_logs(resource_type, resource_id);
CREATE INDEX idx_data_access_owner ON data_access_logs(resource_owner_id);
CREATE INDEX idx_data_access_created ON data_access_logs(created_at DESC);

-- ============================================================================
-- TABLA: compliance_logs
-- Descripción: Logs de cumplimiento regulatorio
-- ============================================================================
CREATE TABLE IF NOT EXISTS compliance_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

    -- Usuario
    user_id UUID REFERENCES auth.users(id),

    -- Evento
    event_type VARCHAR(100) NOT NULL,
    -- 'terms_accepted', 'risk_disclosure_accepted', 'kyc_submitted', 'kyc_approved',
    -- 'aml_check_passed', 'suspicious_activity_flagged', etc.

    -- Detalles
    description TEXT,
    document_version VARCHAR(50),
    document_url TEXT,

    -- Metadata
    metadata JSONB,
    ip_address INET,

    -- Estado de revisión
    requires_review BOOLEAN DEFAULT FALSE,
    reviewed_by UUID REFERENCES auth.users(id),
    reviewed_at TIMESTAMPTZ,
    review_notes TEXT,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_compliance_user ON compliance_logs(user_id);
CREATE INDEX idx_compliance_event ON compliance_logs(event_type);
CREATE INDEX idx_compliance_review ON compliance_logs(requires_review) WHERE requires_review = TRUE;
CREATE INDEX idx_compliance_created ON compliance_logs(created_at DESC);

-- ============================================================================
-- VISTAS DE ADMINISTRACIÓN
-- ============================================================================

-- Vista: Actividad reciente de usuarios
CREATE OR REPLACE VIEW admin_user_activity AS
SELECT
    u.id AS user_id,
    u.email,
    p.first_name,
    p.last_name,
    u.role,
    u.status,
    u.last_login_at,
    u.last_login_ip,
    (SELECT COUNT(*) FROM audit.security_events se WHERE se.user_id = u.id AND se.created_at > NOW() - INTERVAL '24 hours') AS security_events_24h,
    (SELECT COUNT(*) FROM audit.api_request_logs ar WHERE ar.user_id = u.id AND ar.created_at > NOW() - INTERVAL '24 hours') AS api_requests_24h
FROM auth.users u
LEFT JOIN public.profiles p ON p.user_id = u.id;

-- Vista: Alertas de seguridad pendientes
CREATE OR REPLACE VIEW admin_security_alerts AS
SELECT
    se.*,
    u.email AS user_email,
    p.first_name,
    p.last_name
FROM audit.security_events se
LEFT JOIN auth.users u ON u.id = se.user_id
LEFT JOIN public.profiles p ON p.user_id = se.user_id
WHERE se.acknowledged = FALSE
  AND se.severity IN ('warning', 'error', 'critical')
ORDER BY se.created_at DESC;

-- Vista: Resumen de trading por bot
CREATE OR REPLACE VIEW admin_bot_trading_summary AS
SELECT
    ta.bot_id,
    b.name AS bot_name,
    b.risk_profile,
    DATE(ta.created_at) AS date,
    COUNT(*) FILTER (WHERE ta.event_type = 'signal_generated') AS signals_generated,
    COUNT(*) FILTER (WHERE ta.event_type = 'order_placed') AS orders_placed,
    COUNT(*) FILTER (WHERE ta.event_type = 'order_filled') AS orders_filled,
    SUM(ta.pnl) AS total_pnl,
    COUNT(*) FILTER (WHERE ta.pnl > 0) AS winning_trades,
    COUNT(*) FILTER (WHERE ta.pnl < 0) AS losing_trades
FROM audit.trading_audit ta
LEFT JOIN trading.bots b ON b.id = ta.bot_id
WHERE ta.bot_id IS NOT NULL
GROUP BY ta.bot_id, b.name, b.risk_profile, DATE(ta.created_at);

-- ============================================================================
-- FUNCIONES DE LIMPIEZA
-- ============================================================================

-- Función para limpiar logs antiguos
CREATE OR REPLACE FUNCTION cleanup_old_logs(retention_days INT DEFAULT 90)
RETURNS TABLE(
    table_name TEXT,
    rows_deleted BIGINT
) AS $$
DECLARE
    cutoff_date TIMESTAMPTZ;
BEGIN
    cutoff_date := NOW() - (retention_days || ' days')::INTERVAL;

    -- API request logs (30 días por defecto)
    DELETE FROM audit.api_request_logs WHERE created_at < cutoff_date;
    RETURN QUERY SELECT 'api_request_logs'::TEXT, COUNT(*)::BIGINT FROM audit.api_request_logs WHERE created_at < cutoff_date;

    -- Audit logs (90 días por defecto, excepto críticos)
    DELETE FROM audit.audit_logs WHERE created_at < cutoff_date;

    -- Security events (mantener indefinidamente los críticos)
    DELETE FROM audit.security_events
    WHERE created_at < cutoff_date
      AND severity NOT IN ('error', 'critical');

END;
$$ LANGUAGE plpgsql;
