-- =============================================================================
-- MICHANGARRITO - 19 AUDIT (MCH-031: Auditoria Empresarial)
-- =============================================================================
-- Sistema de auditoria y logs para compliance y trazabilidad
-- Registra todas las acciones significativas en el sistema
-- =============================================================================

-- Tipos de accion auditada
CREATE TYPE audit_action AS ENUM (
    -- CRUD generales
    'create',
    'read',
    'update',
    'delete',

    -- Autenticacion
    'login',
    'logout',
    'login_failed',
    'password_change',
    'pin_change',

    -- Ventas
    'sale_create',
    'sale_cancel',
    'sale_refund',

    -- Inventario
    'stock_adjust',
    'stock_transfer',

    -- Fiados
    'credit_grant',
    'credit_payment',
    'credit_writeoff',

    -- Configuracion
    'settings_change',
    'permissions_change',

    -- Exportacion
    'data_export',
    'report_generate'
);

-- Tipos de recurso
CREATE TYPE audit_resource_type AS ENUM (
    'user',
    'tenant',
    'product',
    'category',
    'sale',
    'payment',
    'customer',
    'credit',
    'order',
    'subscription',
    'webhook',
    'settings',
    'report'
);

-- Logs de auditoria (tabla principal)
CREATE TABLE IF NOT EXISTS audit.logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,

    -- Actor
    user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL,
    user_name VARCHAR(100),  -- Snapshot del nombre (por si se elimina usuario)

    -- Accion
    action audit_action NOT NULL,

    -- Recurso afectado
    resource_type audit_resource_type NOT NULL,
    resource_id UUID,
    resource_name VARCHAR(255),  -- Snapshot del nombre

    -- Cambios
    old_values JSONB,  -- Valores anteriores (para updates)
    new_values JSONB,  -- Valores nuevos

    -- Contexto
    ip_address VARCHAR(45),
    user_agent TEXT,
    request_id VARCHAR(100),  -- Para correlacion de logs

    -- Metadata adicional
    metadata JSONB DEFAULT '{}',

    -- Timestamp (sin updated_at, los logs son inmutables)
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- Indices optimizados para consultas frecuentes
CREATE INDEX idx_audit_logs_tenant ON audit.logs(tenant_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_resource ON audit.logs(resource_type, resource_id);
CREATE INDEX idx_audit_logs_created ON audit.logs(created_at DESC);
CREATE INDEX idx_audit_logs_tenant_created ON audit.logs(tenant_id, created_at DESC);

-- Indice para busquedas por rango de fecha (particionado logico)
-- Usa funcion IMMUTABLE timestamptz_to_date() definida en 02-functions.sql
CREATE INDEX idx_audit_logs_date ON audit.logs(public.timestamptz_to_date(created_at));

COMMENT ON TABLE audit.logs IS 'Logs de auditoria inmutables para compliance';
COMMENT ON COLUMN audit.logs.old_values IS 'Snapshot de valores antes del cambio';
COMMENT ON COLUMN audit.logs.new_values IS 'Snapshot de valores despues del cambio';
COMMENT ON COLUMN audit.logs.request_id IS 'ID de correlacion para rastrear requests';

-- Politicas de retencion por tenant
CREATE TABLE IF NOT EXISTS audit.retention_policies (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE UNIQUE,

    -- Configuracion de retencion
    retention_days INTEGER NOT NULL DEFAULT 90,

    -- Por tipo de accion (override)
    retention_overrides JSONB DEFAULT '{}',
    -- Ejemplo: {"login": 30, "sale_create": 365, "data_export": 180}

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

CREATE TRIGGER update_audit_retention_policies_updated_at
    BEFORE UPDATE ON audit.retention_policies
    FOR EACH ROW EXECUTE FUNCTION update_updated_at();

COMMENT ON TABLE audit.retention_policies IS 'Politicas de retencion de logs por tenant';
COMMENT ON COLUMN audit.retention_policies.retention_days IS 'Dias de retencion por defecto';
COMMENT ON COLUMN audit.retention_policies.retention_overrides IS 'Override por tipo de accion: {"login": 30}';

-- =============================================================================
-- FUNCIONES DE AUDITORIA
-- =============================================================================

-- Funcion para registrar log de auditoria
CREATE OR REPLACE FUNCTION audit.log_action(
    p_tenant_id UUID,
    p_user_id UUID,
    p_user_name VARCHAR(100),
    p_action audit_action,
    p_resource_type audit_resource_type,
    p_resource_id UUID DEFAULT NULL,
    p_resource_name VARCHAR(255) DEFAULT NULL,
    p_old_values JSONB DEFAULT NULL,
    p_new_values JSONB DEFAULT NULL,
    p_ip_address VARCHAR(45) DEFAULT NULL,
    p_user_agent TEXT DEFAULT NULL,
    p_request_id VARCHAR(100) DEFAULT NULL,
    p_metadata JSONB DEFAULT '{}'
) RETURNS UUID AS $$
DECLARE
    v_log_id UUID;
BEGIN
    INSERT INTO audit.logs (
        tenant_id, user_id, user_name, action,
        resource_type, resource_id, resource_name,
        old_values, new_values,
        ip_address, user_agent, request_id, metadata
    ) VALUES (
        p_tenant_id, p_user_id, p_user_name, p_action,
        p_resource_type, p_resource_id, p_resource_name,
        p_old_values, p_new_values,
        p_ip_address, p_user_agent, p_request_id, p_metadata
    ) RETURNING id INTO v_log_id;

    RETURN v_log_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION audit.log_action IS 'Registra una accion en el log de auditoria';

-- Funcion para limpiar logs antiguos segun politica de retencion
CREATE OR REPLACE FUNCTION audit.cleanup_old_logs()
RETURNS INTEGER AS $$
DECLARE
    v_deleted INTEGER := 0;
    v_tenant RECORD;
BEGIN
    FOR v_tenant IN
        SELECT t.id as tenant_id, COALESCE(p.retention_days, 90) as retention_days
        FROM public.tenants t
        LEFT JOIN audit.retention_policies p ON p.tenant_id = t.id
    LOOP
        DELETE FROM audit.logs
        WHERE tenant_id = v_tenant.tenant_id
          AND created_at < NOW() - (v_tenant.retention_days || ' days')::INTERVAL;

        v_deleted := v_deleted + (SELECT COUNT(*) FROM audit.logs WHERE tenant_id = v_tenant.tenant_id);
    END LOOP;

    RETURN v_deleted;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION audit.cleanup_old_logs IS 'Elimina logs antiguos segun politicas de retencion';
