-- =============================================================
-- ARCHIVO: 10-audit.sql
-- DESCRIPCION: Sistema de audit trail, cambios de entidades, logs
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-01-10
-- EPIC: SAAS-AUDIT (EPIC-SAAS-004)
-- HISTORIAS: US-050, US-051, US-052
-- =============================================================

-- =====================
-- SCHEMA: audit
-- =====================
CREATE SCHEMA IF NOT EXISTS audit;

-- =====================
-- TABLA: audit.audit_logs
-- Log de auditoría general
-- =====================
CREATE TABLE IF NOT EXISTS audit.audit_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Actor
    user_id UUID REFERENCES auth.users(id),
    user_email VARCHAR(255),
    user_name VARCHAR(200),
    session_id UUID,
    impersonator_id UUID REFERENCES auth.users(id), -- Si está siendo impersonado

    -- Acción
    action VARCHAR(50) NOT NULL, -- create, read, update, delete, login, logout, export, etc.
    action_category VARCHAR(50), -- data, auth, system, config, billing

    -- Recurso
    resource_type VARCHAR(100) NOT NULL, -- user, product, sale, branch, etc.
    resource_id UUID,
    resource_name VARCHAR(255),

    -- Cambios
    old_values JSONB,
    new_values JSONB,
    changed_fields TEXT[],

    -- Contexto
    ip_address INET,
    user_agent TEXT,
    device_info JSONB DEFAULT '{}',
    location JSONB DEFAULT '{}', -- {country, city, lat, lng}

    -- Request info
    request_id VARCHAR(100),
    request_method VARCHAR(10),
    request_path TEXT,
    request_params JSONB DEFAULT '{}',

    -- Resultado
    status VARCHAR(20) DEFAULT 'success', -- success, failure, partial
    error_message TEXT,
    duration_ms INTEGER,

    -- Metadata
    metadata JSONB DEFAULT '{}',
    tags TEXT[] DEFAULT '{}',

    -- Timestamp
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Particionar por fecha para mejor rendimiento (recomendado en producción)
-- CREATE TABLE audit.audit_logs_y2026m01 PARTITION OF audit.audit_logs
--     FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');

-- =====================
-- TABLA: audit.entity_changes
-- Historial detallado de cambios por entidad
-- =====================
CREATE TABLE IF NOT EXISTS audit.entity_changes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Entidad
    entity_type VARCHAR(100) NOT NULL,
    entity_id UUID NOT NULL,
    entity_name VARCHAR(255),

    -- Versión
    version INTEGER NOT NULL DEFAULT 1,
    previous_version INTEGER,

    -- Snapshot completo de la entidad
    data_snapshot JSONB NOT NULL,

    -- Cambios específicos
    changes JSONB DEFAULT '[]',
    -- Ejemplo: [{"field": "price", "old": 100, "new": 150, "type": "update"}]

    -- Actor
    changed_by UUID REFERENCES auth.users(id),
    change_reason TEXT,

    -- Tipo de cambio
    change_type VARCHAR(20) NOT NULL, -- create, update, delete, restore

    -- Timestamp
    changed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Índice único para versiones
CREATE UNIQUE INDEX IF NOT EXISTS idx_entity_version ON audit.entity_changes(entity_type, entity_id, version);

-- =====================
-- TABLA: audit.sensitive_data_access
-- Acceso a datos sensibles
-- =====================
CREATE TABLE IF NOT EXISTS audit.sensitive_data_access (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Actor
    user_id UUID NOT NULL REFERENCES auth.users(id),
    session_id UUID,

    -- Datos accedidos
    data_type VARCHAR(100) NOT NULL, -- pii, financial, medical, credentials
    data_category VARCHAR(100), -- customer_data, employee_data, payment_info
    entity_type VARCHAR(100),
    entity_id UUID,

    -- Acción
    access_type VARCHAR(30) NOT NULL, -- view, export, modify, decrypt
    access_reason TEXT,

    -- Contexto
    ip_address INET,
    user_agent TEXT,

    -- Resultado
    was_authorized BOOLEAN DEFAULT TRUE,
    denial_reason TEXT,

    -- Timestamp
    accessed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: audit.data_exports
-- Log de exportaciones de datos
-- =====================
CREATE TABLE IF NOT EXISTS audit.data_exports (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Actor
    user_id UUID NOT NULL REFERENCES auth.users(id),

    -- Exportación
    export_type VARCHAR(50) NOT NULL, -- report, backup, gdpr_request, bulk_export
    export_format VARCHAR(20), -- csv, xlsx, pdf, json
    entity_types TEXT[] NOT NULL,

    -- Filtros aplicados
    filters JSONB DEFAULT '{}',
    date_range_start TIMESTAMPTZ,
    date_range_end TIMESTAMPTZ,

    -- Resultado
    record_count INTEGER,
    file_size_bytes BIGINT,
    file_hash VARCHAR(64), -- SHA-256 del archivo

    -- Estado
    status VARCHAR(20) DEFAULT 'pending', -- pending, processing, completed, failed, expired

    -- Archivos
    download_url TEXT,
    download_expires_at TIMESTAMPTZ,
    download_count INTEGER DEFAULT 0,

    -- Contexto
    ip_address INET,
    user_agent TEXT,

    -- Timestamps
    requested_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    completed_at TIMESTAMPTZ,
    expires_at TIMESTAMPTZ
);

-- =====================
-- TABLA: audit.login_history
-- Historial de inicios de sesión
-- =====================
CREATE TABLE IF NOT EXISTS audit.login_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE,
    user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE,

    -- Identificación del usuario
    email VARCHAR(255),
    username VARCHAR(100),

    -- Resultado
    status VARCHAR(20) NOT NULL, -- success, failed, blocked, mfa_required, mfa_failed

    -- Método de autenticación
    auth_method VARCHAR(30), -- password, sso, oauth, mfa, magic_link, biometric
    oauth_provider VARCHAR(30),

    -- MFA
    mfa_method VARCHAR(20), -- totp, sms, email, push
    mfa_verified BOOLEAN,

    -- Dispositivo
    device_id UUID REFERENCES auth.devices(id),
    device_fingerprint VARCHAR(255),
    device_type VARCHAR(30), -- desktop, mobile, tablet
    device_os VARCHAR(50),
    device_browser VARCHAR(50),

    -- Ubicación
    ip_address INET,
    user_agent TEXT,
    country_code VARCHAR(2),
    city VARCHAR(100),
    latitude DECIMAL(10, 8),
    longitude DECIMAL(11, 8),

    -- Riesgo
    risk_score INTEGER, -- 0-100
    risk_factors JSONB DEFAULT '[]',
    is_suspicious BOOLEAN DEFAULT FALSE,
    is_new_device BOOLEAN DEFAULT FALSE,
    is_new_location BOOLEAN DEFAULT FALSE,

    -- Error info
    failure_reason VARCHAR(100),
    failure_count INTEGER,

    -- Timestamp
    attempted_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: audit.permission_changes
-- Cambios en permisos y roles
-- =====================
CREATE TABLE IF NOT EXISTS audit.permission_changes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Actor
    changed_by UUID NOT NULL REFERENCES auth.users(id),

    -- Usuario afectado
    target_user_id UUID NOT NULL REFERENCES auth.users(id),
    target_user_email VARCHAR(255),

    -- Tipo de cambio
    change_type VARCHAR(30) NOT NULL, -- role_assigned, role_revoked, permission_granted, permission_revoked

    -- Rol/Permiso
    role_id UUID,
    role_code VARCHAR(50),
    permission_id UUID,
    permission_code VARCHAR(100),

    -- Contexto
    branch_id UUID REFERENCES core.branches(id),
    scope VARCHAR(30), -- global, tenant, branch

    -- Valores anteriores
    previous_roles TEXT[],
    previous_permissions TEXT[],

    -- Razón
    reason TEXT,

    -- Timestamp
    changed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: audit.config_changes
-- Cambios en configuración del sistema
-- =====================
CREATE TABLE IF NOT EXISTS audit.config_changes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE, -- NULL = config global

    -- Actor
    changed_by UUID NOT NULL REFERENCES auth.users(id),

    -- Configuración
    config_type VARCHAR(50) NOT NULL, -- tenant_settings, user_settings, system_settings, feature_flags
    config_key VARCHAR(100) NOT NULL,
    config_path TEXT, -- Path jerárquico: billing.invoicing.prefix

    -- Valores
    old_value JSONB,
    new_value JSONB,

    -- Contexto
    reason TEXT,
    ticket_id VARCHAR(50), -- Referencia a ticket de soporte

    -- Timestamp
    changed_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- INDICES
-- =====================

-- Indices para audit_logs
CREATE INDEX IF NOT EXISTS idx_audit_logs_tenant ON audit.audit_logs(tenant_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_user ON audit.audit_logs(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_action ON audit.audit_logs(action);
CREATE INDEX IF NOT EXISTS idx_audit_logs_resource ON audit.audit_logs(resource_type, resource_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_created ON audit.audit_logs(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_audit_logs_category ON audit.audit_logs(action_category);
CREATE INDEX IF NOT EXISTS idx_audit_logs_status ON audit.audit_logs(status) WHERE status = 'failure';
CREATE INDEX IF NOT EXISTS idx_audit_logs_tags ON audit.audit_logs USING GIN(tags);

-- Indices para entity_changes
CREATE INDEX IF NOT EXISTS idx_entity_changes_tenant ON audit.entity_changes(tenant_id);
CREATE INDEX IF NOT EXISTS idx_entity_changes_entity ON audit.entity_changes(entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_entity_changes_changed_by ON audit.entity_changes(changed_by);
CREATE INDEX IF NOT EXISTS idx_entity_changes_date ON audit.entity_changes(changed_at DESC);

-- Indices para sensitive_data_access
CREATE INDEX IF NOT EXISTS idx_sensitive_access_tenant ON audit.sensitive_data_access(tenant_id);
CREATE INDEX IF NOT EXISTS idx_sensitive_access_user ON audit.sensitive_data_access(user_id);
CREATE INDEX IF NOT EXISTS idx_sensitive_access_type ON audit.sensitive_data_access(data_type);
CREATE INDEX IF NOT EXISTS idx_sensitive_access_date ON audit.sensitive_data_access(accessed_at DESC);
CREATE INDEX IF NOT EXISTS idx_sensitive_unauthorized ON audit.sensitive_data_access(was_authorized)
    WHERE was_authorized = FALSE;

-- Indices para data_exports
CREATE INDEX IF NOT EXISTS idx_exports_tenant ON audit.data_exports(tenant_id);
CREATE INDEX IF NOT EXISTS idx_exports_user ON audit.data_exports(user_id);
CREATE INDEX IF NOT EXISTS idx_exports_status ON audit.data_exports(status);
CREATE INDEX IF NOT EXISTS idx_exports_date ON audit.data_exports(requested_at DESC);

-- Indices para login_history
CREATE INDEX IF NOT EXISTS idx_login_tenant ON audit.login_history(tenant_id);
CREATE INDEX IF NOT EXISTS idx_login_user ON audit.login_history(user_id);
CREATE INDEX IF NOT EXISTS idx_login_status ON audit.login_history(status);
CREATE INDEX IF NOT EXISTS idx_login_date ON audit.login_history(attempted_at DESC);
CREATE INDEX IF NOT EXISTS idx_login_ip ON audit.login_history(ip_address);
CREATE INDEX IF NOT EXISTS idx_login_suspicious ON audit.login_history(is_suspicious) WHERE is_suspicious = TRUE;
CREATE INDEX IF NOT EXISTS idx_login_failed ON audit.login_history(status, email) WHERE status = 'failed';

-- Indices para permission_changes
CREATE INDEX IF NOT EXISTS idx_perm_changes_tenant ON audit.permission_changes(tenant_id);
CREATE INDEX IF NOT EXISTS idx_perm_changes_target ON audit.permission_changes(target_user_id);
CREATE INDEX IF NOT EXISTS idx_perm_changes_date ON audit.permission_changes(changed_at DESC);

-- Indices para config_changes
CREATE INDEX IF NOT EXISTS idx_config_changes_tenant ON audit.config_changes(tenant_id);
CREATE INDEX IF NOT EXISTS idx_config_changes_type ON audit.config_changes(config_type);
CREATE INDEX IF NOT EXISTS idx_config_changes_date ON audit.config_changes(changed_at DESC);

-- =====================
-- RLS POLICIES
-- =====================

ALTER TABLE audit.audit_logs ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_audit_logs ON audit.audit_logs
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE audit.entity_changes ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_entity_changes ON audit.entity_changes
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE audit.sensitive_data_access ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_sensitive ON audit.sensitive_data_access
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE audit.data_exports ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_exports ON audit.data_exports
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE audit.login_history ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_login ON audit.login_history
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE audit.permission_changes ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_perm_changes ON audit.permission_changes
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

ALTER TABLE audit.config_changes ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_config_changes ON audit.config_changes
    USING (
        tenant_id IS NULL
        OR tenant_id = current_setting('app.current_tenant_id', true)::uuid
    );

-- =====================
-- FUNCIONES
-- =====================

-- Función para registrar log de auditoría
CREATE OR REPLACE FUNCTION audit.log(
    p_tenant_id UUID,
    p_user_id UUID,
    p_action VARCHAR(50),
    p_resource_type VARCHAR(100),
    p_resource_id UUID DEFAULT NULL,
    p_old_values JSONB DEFAULT NULL,
    p_new_values JSONB DEFAULT NULL,
    p_metadata JSONB DEFAULT '{}'
)
RETURNS UUID AS $$
DECLARE
    v_log_id UUID;
    v_changed_fields TEXT[];
BEGIN
    -- Calcular campos cambiados
    IF p_old_values IS NOT NULL AND p_new_values IS NOT NULL THEN
        SELECT ARRAY_AGG(key)
        INTO v_changed_fields
        FROM (
            SELECT key FROM jsonb_object_keys(p_old_values) AS key
            WHERE p_old_values->key IS DISTINCT FROM p_new_values->key
            UNION
            SELECT key FROM jsonb_object_keys(p_new_values) AS key
            WHERE NOT p_old_values ? key
        ) AS changed;
    END IF;

    INSERT INTO audit.audit_logs (
        tenant_id, user_id, action, resource_type, resource_id,
        old_values, new_values, changed_fields, metadata
    ) VALUES (
        p_tenant_id, p_user_id, p_action, p_resource_type, p_resource_id,
        p_old_values, p_new_values, v_changed_fields, p_metadata
    ) RETURNING id INTO v_log_id;

    RETURN v_log_id;
END;
$$ LANGUAGE plpgsql;

-- Función para registrar cambio de entidad con versionamiento
CREATE OR REPLACE FUNCTION audit.log_entity_change(
    p_tenant_id UUID,
    p_entity_type VARCHAR(100),
    p_entity_id UUID,
    p_data_snapshot JSONB,
    p_changes JSONB DEFAULT '[]',
    p_changed_by UUID DEFAULT NULL,
    p_change_type VARCHAR(20) DEFAULT 'update',
    p_change_reason TEXT DEFAULT NULL
)
RETURNS INTEGER AS $$
DECLARE
    v_version INTEGER;
    v_prev_version INTEGER;
BEGIN
    -- Obtener versión actual
    SELECT COALESCE(MAX(version), 0) INTO v_prev_version
    FROM audit.entity_changes
    WHERE entity_type = p_entity_type AND entity_id = p_entity_id;

    v_version := v_prev_version + 1;

    INSERT INTO audit.entity_changes (
        tenant_id, entity_type, entity_id, version, previous_version,
        data_snapshot, changes, changed_by, change_type, change_reason
    ) VALUES (
        p_tenant_id, p_entity_type, p_entity_id, v_version, v_prev_version,
        p_data_snapshot, p_changes, p_changed_by, p_change_type, p_change_reason
    );

    RETURN v_version;
END;
$$ LANGUAGE plpgsql;

-- Función para obtener historial de una entidad
CREATE OR REPLACE FUNCTION audit.get_entity_history(
    p_entity_type VARCHAR(100),
    p_entity_id UUID,
    p_limit INTEGER DEFAULT 50
)
RETURNS TABLE (
    version INTEGER,
    change_type VARCHAR(20),
    data_snapshot JSONB,
    changes JSONB,
    changed_by UUID,
    change_reason TEXT,
    changed_at TIMESTAMPTZ
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        ec.version,
        ec.change_type,
        ec.data_snapshot,
        ec.changes,
        ec.changed_by,
        ec.change_reason,
        ec.changed_at
    FROM audit.entity_changes ec
    WHERE ec.entity_type = p_entity_type
    AND ec.entity_id = p_entity_id
    ORDER BY ec.version DESC
    LIMIT p_limit;
END;
$$ LANGUAGE plpgsql STABLE;

-- Función para obtener snapshot de una entidad en un momento dado
CREATE OR REPLACE FUNCTION audit.get_entity_at_time(
    p_entity_type VARCHAR(100),
    p_entity_id UUID,
    p_at_time TIMESTAMPTZ
)
RETURNS JSONB AS $$
BEGIN
    RETURN (
        SELECT data_snapshot
        FROM audit.entity_changes
        WHERE entity_type = p_entity_type
        AND entity_id = p_entity_id
        AND changed_at <= p_at_time
        ORDER BY changed_at DESC
        LIMIT 1
    );
END;
$$ LANGUAGE plpgsql STABLE;

-- Función para registrar acceso a datos sensibles
CREATE OR REPLACE FUNCTION audit.log_sensitive_access(
    p_tenant_id UUID,
    p_user_id UUID,
    p_data_type VARCHAR(100),
    p_access_type VARCHAR(30),
    p_entity_type VARCHAR(100) DEFAULT NULL,
    p_entity_id UUID DEFAULT NULL,
    p_was_authorized BOOLEAN DEFAULT TRUE,
    p_access_reason TEXT DEFAULT NULL
)
RETURNS UUID AS $$
DECLARE
    v_access_id UUID;
BEGIN
    INSERT INTO audit.sensitive_data_access (
        tenant_id, user_id, data_type, access_type,
        entity_type, entity_id, was_authorized, access_reason
    ) VALUES (
        p_tenant_id, p_user_id, p_data_type, p_access_type,
        p_entity_type, p_entity_id, p_was_authorized, p_access_reason
    ) RETURNING id INTO v_access_id;

    RETURN v_access_id;
END;
$$ LANGUAGE plpgsql;

-- Función para registrar login
CREATE OR REPLACE FUNCTION audit.log_login(
    p_user_id UUID,
    p_tenant_id UUID,
    p_email VARCHAR(255),
    p_status VARCHAR(20),
    p_auth_method VARCHAR(30) DEFAULT 'password',
    p_ip_address INET DEFAULT NULL,
    p_user_agent TEXT DEFAULT NULL,
    p_device_info JSONB DEFAULT '{}'
)
RETURNS UUID AS $$
DECLARE
    v_login_id UUID;
    v_is_new_device BOOLEAN := FALSE;
    v_is_new_location BOOLEAN := FALSE;
    v_failure_count INTEGER := 0;
BEGIN
    -- Verificar si es dispositivo nuevo
    IF p_device_info->>'fingerprint' IS NOT NULL THEN
        SELECT NOT EXISTS (
            SELECT 1 FROM audit.login_history
            WHERE user_id = p_user_id
            AND device_fingerprint = p_device_info->>'fingerprint'
            AND status = 'success'
        ) INTO v_is_new_device;
    END IF;

    -- Contar intentos fallidos recientes
    IF p_status = 'failed' THEN
        SELECT COUNT(*) INTO v_failure_count
        FROM audit.login_history
        WHERE email = p_email
        AND status = 'failed'
        AND attempted_at > CURRENT_TIMESTAMP - INTERVAL '1 hour';
    END IF;

    INSERT INTO audit.login_history (
        user_id, tenant_id, email, status, auth_method,
        ip_address, user_agent,
        device_fingerprint, device_type, device_os, device_browser,
        is_new_device, failure_count
    ) VALUES (
        p_user_id, p_tenant_id, p_email, p_status, p_auth_method,
        p_ip_address, p_user_agent,
        p_device_info->>'fingerprint',
        p_device_info->>'type',
        p_device_info->>'os',
        p_device_info->>'browser',
        v_is_new_device, v_failure_count
    ) RETURNING id INTO v_login_id;

    RETURN v_login_id;
END;
$$ LANGUAGE plpgsql;

-- Función para obtener estadísticas de auditoría
CREATE OR REPLACE FUNCTION audit.get_stats(
    p_tenant_id UUID,
    p_days INTEGER DEFAULT 30
)
RETURNS TABLE (
    total_actions BIGINT,
    unique_users BIGINT,
    actions_by_category JSONB,
    actions_by_day JSONB,
    top_resources JSONB,
    failed_actions BIGINT
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        COUNT(*) as total_actions,
        COUNT(DISTINCT al.user_id) as unique_users,
        jsonb_object_agg(COALESCE(al.action_category, 'other'), cat_count) as actions_by_category,
        jsonb_object_agg(day_date, day_count) as actions_by_day,
        jsonb_agg(DISTINCT jsonb_build_object('type', al.resource_type, 'count', res_count)) as top_resources,
        COUNT(*) FILTER (WHERE al.status = 'failure') as failed_actions
    FROM audit.audit_logs al
    LEFT JOIN (
        SELECT action_category, COUNT(*) as cat_count
        FROM audit.audit_logs
        WHERE tenant_id = p_tenant_id
        AND created_at > CURRENT_TIMESTAMP - (p_days || ' days')::INTERVAL
        GROUP BY action_category
    ) cat ON cat.action_category = al.action_category
    LEFT JOIN (
        SELECT DATE(created_at) as day_date, COUNT(*) as day_count
        FROM audit.audit_logs
        WHERE tenant_id = p_tenant_id
        AND created_at > CURRENT_TIMESTAMP - (p_days || ' days')::INTERVAL
        GROUP BY DATE(created_at)
    ) days ON TRUE
    LEFT JOIN (
        SELECT resource_type, COUNT(*) as res_count
        FROM audit.audit_logs
        WHERE tenant_id = p_tenant_id
        AND created_at > CURRENT_TIMESTAMP - (p_days || ' days')::INTERVAL
        GROUP BY resource_type
        ORDER BY res_count DESC
        LIMIT 10
    ) res ON res.resource_type = al.resource_type
    WHERE al.tenant_id = p_tenant_id
    AND al.created_at > CURRENT_TIMESTAMP - (p_days || ' days')::INTERVAL
    LIMIT 1;
END;
$$ LANGUAGE plpgsql STABLE;

-- Función para limpiar logs antiguos
CREATE OR REPLACE FUNCTION audit.cleanup_old_logs(
    p_audit_days INTEGER DEFAULT 365,
    p_login_days INTEGER DEFAULT 90,
    p_export_days INTEGER DEFAULT 30
)
RETURNS TABLE (
    audit_deleted INTEGER,
    login_deleted INTEGER,
    export_deleted INTEGER
) AS $$
DECLARE
    v_audit INTEGER;
    v_login INTEGER;
    v_export INTEGER;
BEGIN
    -- Limpiar audit_logs
    DELETE FROM audit.audit_logs
    WHERE created_at < CURRENT_TIMESTAMP - (p_audit_days || ' days')::INTERVAL;
    GET DIAGNOSTICS v_audit = ROW_COUNT;

    -- Limpiar login_history
    DELETE FROM audit.login_history
    WHERE attempted_at < CURRENT_TIMESTAMP - (p_login_days || ' days')::INTERVAL;
    GET DIAGNOSTICS v_login = ROW_COUNT;

    -- Limpiar data_exports completados/expirados
    DELETE FROM audit.data_exports
    WHERE (status IN ('completed', 'expired', 'failed'))
    AND requested_at < CURRENT_TIMESTAMP - (p_export_days || ' days')::INTERVAL;
    GET DIAGNOSTICS v_export = ROW_COUNT;

    RETURN QUERY SELECT v_audit, v_login, v_export;
END;
$$ LANGUAGE plpgsql;

-- =====================
-- TRIGGER GENÉRICO PARA AUDITORÍA
-- =====================

-- Función trigger para auditoría automática
CREATE OR REPLACE FUNCTION audit.audit_trigger_func()
RETURNS TRIGGER AS $$
DECLARE
    v_old_data JSONB;
    v_new_data JSONB;
    v_tenant_id UUID;
    v_user_id UUID;
BEGIN
    -- Obtener tenant_id y user_id del contexto
    v_tenant_id := current_setting('app.current_tenant_id', true)::uuid;
    v_user_id := current_setting('app.current_user_id', true)::uuid;

    IF TG_OP = 'INSERT' THEN
        v_new_data := to_jsonb(NEW);

        PERFORM audit.log_entity_change(
            v_tenant_id,
            TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
            (v_new_data->>'id')::uuid,
            v_new_data,
            '[]'::jsonb,
            v_user_id,
            'create'
        );

        RETURN NEW;

    ELSIF TG_OP = 'UPDATE' THEN
        v_old_data := to_jsonb(OLD);
        v_new_data := to_jsonb(NEW);

        -- Solo registrar si hay cambios reales
        IF v_old_data IS DISTINCT FROM v_new_data THEN
            PERFORM audit.log_entity_change(
                v_tenant_id,
                TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
                (v_new_data->>'id')::uuid,
                v_new_data,
                jsonb_build_array(jsonb_build_object(
                    'old', v_old_data,
                    'new', v_new_data
                )),
                v_user_id,
                'update'
            );
        END IF;

        RETURN NEW;

    ELSIF TG_OP = 'DELETE' THEN
        v_old_data := to_jsonb(OLD);

        PERFORM audit.log_entity_change(
            v_tenant_id,
            TG_TABLE_SCHEMA || '.' || TG_TABLE_NAME,
            (v_old_data->>'id')::uuid,
            v_old_data,
            '[]'::jsonb,
            v_user_id,
            'delete'
        );

        RETURN OLD;
    END IF;

    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- =====================
-- COMENTARIOS
-- =====================
COMMENT ON TABLE audit.audit_logs IS 'Log de auditoría general para todas las acciones';
COMMENT ON TABLE audit.entity_changes IS 'Historial de cambios con versionamiento por entidad';
COMMENT ON TABLE audit.sensitive_data_access IS 'Log de acceso a datos sensibles';
COMMENT ON TABLE audit.data_exports IS 'Registro de exportaciones de datos';
COMMENT ON TABLE audit.login_history IS 'Historial de intentos de inicio de sesión';
COMMENT ON TABLE audit.permission_changes IS 'Log de cambios en permisos y roles';
COMMENT ON TABLE audit.config_changes IS 'Log de cambios en configuración del sistema';

COMMENT ON FUNCTION audit.log IS 'Registra una acción en el log de auditoría';
COMMENT ON FUNCTION audit.log_entity_change IS 'Registra un cambio versionado de una entidad';
COMMENT ON FUNCTION audit.get_entity_history IS 'Obtiene el historial de cambios de una entidad';
COMMENT ON FUNCTION audit.get_entity_at_time IS 'Obtiene el snapshot de una entidad en un momento específico';
COMMENT ON FUNCTION audit.log_login IS 'Registra un intento de inicio de sesión';
COMMENT ON FUNCTION audit.audit_trigger_func IS 'Función trigger para auditoría automática de tablas';
