-- =============================================================
-- ARCHIVO: 30-settings.sql
-- DESCRIPCION: Sistema de Settings para configuraciones multi-nivel
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core
-- FECHA: 2026-01-26
-- MODULO: MGN-006 (Settings)
-- RF: RF-SETTINGS-001, RF-SETTINGS-002, RF-SETTINGS-003
-- NOTA: Feature Flags ya existen en 11-feature-flags.sql (schema flags)
-- =============================================================

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

-- =====================
-- TABLA: core_settings.system_settings
-- Configuraciones globales del sistema (RF-SETTINGS-001)
-- =====================
CREATE TABLE IF NOT EXISTS core_settings.system_settings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Identificacion
    key VARCHAR(100) UNIQUE NOT NULL,
    value JSONB NOT NULL,

    -- Metadata
    data_type VARCHAR(20) NOT NULL DEFAULT 'string'
        CHECK (data_type IN ('string', 'number', 'boolean', 'json', 'array', 'secret')),
    category VARCHAR(50) NOT NULL,
    description TEXT,

    -- Control
    is_public BOOLEAN NOT NULL DEFAULT false,
    is_editable BOOLEAN NOT NULL DEFAULT true,
    default_value JSONB,
    validation_rules JSONB DEFAULT '{}',

    -- Audit
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_by UUID REFERENCES auth.users(id)
);

-- Indices para system_settings
CREATE INDEX IF NOT EXISTS idx_system_settings_category
    ON core_settings.system_settings(category);
CREATE INDEX IF NOT EXISTS idx_system_settings_public
    ON core_settings.system_settings(is_public) WHERE is_public = true;
CREATE INDEX IF NOT EXISTS idx_system_settings_editable
    ON core_settings.system_settings(is_editable) WHERE is_editable = true;

-- =====================
-- TABLA: core_settings.plan_settings
-- Configuraciones por defecto por plan de suscripcion (RF-SETTINGS-002)
-- =====================
CREATE TABLE IF NOT EXISTS core_settings.plan_settings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    plan_id UUID NOT NULL REFERENCES billing.subscription_plans(id) ON DELETE CASCADE,
    key VARCHAR(100) NOT NULL,
    value JSONB NOT NULL,

    -- Audit
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    UNIQUE(plan_id, key)
);

-- Indices para plan_settings
CREATE INDEX IF NOT EXISTS idx_plan_settings_plan
    ON core_settings.plan_settings(plan_id);

-- =====================
-- TABLA: core_settings.tenant_settings
-- Configuraciones personalizadas por tenant (RF-SETTINGS-002)
-- =====================
CREATE TABLE IF NOT EXISTS core_settings.tenant_settings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    key VARCHAR(100) NOT NULL,
    value JSONB NOT NULL,

    -- Herencia
    inherited_from VARCHAR(20) NOT NULL DEFAULT 'custom'
        CHECK (inherited_from IN ('system', 'plan', 'custom')),
    is_overridden BOOLEAN NOT NULL DEFAULT true,

    -- Audit
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    UNIQUE(tenant_id, key)
);

-- Indices para tenant_settings
CREATE INDEX IF NOT EXISTS idx_tenant_settings_tenant
    ON core_settings.tenant_settings(tenant_id);
CREATE INDEX IF NOT EXISTS idx_tenant_settings_key
    ON core_settings.tenant_settings(key);

-- RLS para tenant_settings
ALTER TABLE core_settings.tenant_settings ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS tenant_isolation_settings ON core_settings.tenant_settings;
CREATE POLICY tenant_isolation_settings ON core_settings.tenant_settings
    FOR ALL USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- =====================
-- TABLA: core_settings.user_preferences
-- Preferencias personales de usuario (RF-SETTINGS-003)
-- =====================
CREATE TABLE IF NOT EXISTS core_settings.user_preferences (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Relaciones
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
    key VARCHAR(100) NOT NULL,
    value JSONB NOT NULL,

    -- Sync
    synced_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    -- Audit
    created_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,

    UNIQUE(user_id, key)
);

-- Indices para user_preferences
CREATE INDEX IF NOT EXISTS idx_user_preferences_user
    ON core_settings.user_preferences(user_id);
CREATE INDEX IF NOT EXISTS idx_user_preferences_key
    ON core_settings.user_preferences(key);

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

-- Funcion para obtener setting efectivo del tenant (herencia system -> plan -> tenant)
CREATE OR REPLACE FUNCTION core_settings.get_tenant_setting(
    p_tenant_id UUID,
    p_key VARCHAR
) RETURNS JSONB AS $$
DECLARE
    v_value JSONB;
    v_plan_id UUID;
BEGIN
    -- 1. Buscar en tenant_settings (override)
    SELECT value INTO v_value
    FROM core_settings.tenant_settings
    WHERE tenant_id = p_tenant_id
    AND key = p_key
    AND is_overridden = true;

    IF v_value IS NOT NULL THEN
        RETURN v_value;
    END IF;

    -- 2. Buscar en plan_settings
    SELECT t.plan_id INTO v_plan_id
    FROM auth.tenants t WHERE t.id = p_tenant_id;

    IF v_plan_id IS NOT NULL THEN
        SELECT value INTO v_value
        FROM core_settings.plan_settings
        WHERE plan_id = v_plan_id AND key = p_key;

        IF v_value IS NOT NULL THEN
            RETURN v_value;
        END IF;
    END IF;

    -- 3. Retornar system default
    SELECT COALESCE(value, default_value) INTO v_value
    FROM core_settings.system_settings
    WHERE key = p_key;

    RETURN v_value;
END;
$$ LANGUAGE plpgsql STABLE;

-- Funcion para obtener todas las preferencias de un usuario
CREATE OR REPLACE FUNCTION core_settings.get_user_preferences(p_user_id UUID)
RETURNS TABLE (
    key VARCHAR(100),
    value JSONB,
    synced_at TIMESTAMPTZ
) AS $$
BEGIN
    RETURN QUERY
    SELECT up.key, up.value, up.synced_at
    FROM core_settings.user_preferences up
    WHERE up.user_id = p_user_id
    ORDER BY up.key;
END;
$$ LANGUAGE plpgsql STABLE;

-- Funcion para obtener todos los settings de un tenant (merged)
CREATE OR REPLACE FUNCTION core_settings.get_all_tenant_settings(p_tenant_id UUID)
RETURNS TABLE (
    key VARCHAR(100),
    value JSONB,
    source VARCHAR(20)
) AS $$
BEGIN
    RETURN QUERY
    -- System settings publicos
    SELECT ss.key, COALESCE(ss.value, ss.default_value), 'system'::VARCHAR(20)
    FROM core_settings.system_settings ss
    WHERE ss.is_public = true
    AND NOT EXISTS (
        SELECT 1 FROM core_settings.tenant_settings ts
        WHERE ts.tenant_id = p_tenant_id AND ts.key = ss.key AND ts.is_overridden = true
    )
    UNION ALL
    -- Tenant overrides
    SELECT ts.key, ts.value, ts.inherited_from
    FROM core_settings.tenant_settings ts
    WHERE ts.tenant_id = p_tenant_id AND ts.is_overridden = true
    ORDER BY key;
END;
$$ LANGUAGE plpgsql STABLE;

-- =====================
-- TRIGGERS
-- =====================

-- Trigger para updated_at
CREATE OR REPLACE FUNCTION core_settings.update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = CURRENT_TIMESTAMP;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_system_settings_updated_at
    BEFORE UPDATE ON core_settings.system_settings
    FOR EACH ROW
    EXECUTE FUNCTION core_settings.update_timestamp();

CREATE TRIGGER trg_plan_settings_updated_at
    BEFORE UPDATE ON core_settings.plan_settings
    FOR EACH ROW
    EXECUTE FUNCTION core_settings.update_timestamp();

CREATE TRIGGER trg_tenant_settings_updated_at
    BEFORE UPDATE ON core_settings.tenant_settings
    FOR EACH ROW
    EXECUTE FUNCTION core_settings.update_timestamp();

CREATE TRIGGER trg_user_preferences_updated_at
    BEFORE UPDATE ON core_settings.user_preferences
    FOR EACH ROW
    EXECUTE FUNCTION core_settings.update_timestamp();

-- =====================
-- SEED DATA: System Settings Base
-- =====================
INSERT INTO core_settings.system_settings (key, value, data_type, category, description, is_public, is_editable, default_value) VALUES
-- Security
('security.max_login_attempts', '5', 'number', 'security', 'Intentos maximos de login antes de bloqueo', true, true, '5'),
('security.lockout_duration_minutes', '15', 'number', 'security', 'Duracion de bloqueo en minutos', true, true, '15'),
('security.session_timeout_hours', '24', 'number', 'security', 'Timeout de sesion en horas', true, true, '24'),
('security.password_min_length', '8', 'number', 'security', 'Longitud minima de password', true, true, '8'),
('security.require_mfa', 'false', 'boolean', 'security', 'Requerir MFA para todos los usuarios', true, true, 'false'),
('security.allowed_ip_ranges', '[]', 'array', 'security', 'Rangos IP permitidos (vacio = todos)', false, true, '[]'),

-- Email
('email.smtp_host', '""', 'string', 'email', 'Host SMTP para envio de emails', false, true, '""'),
('email.smtp_port', '587', 'number', 'email', 'Puerto SMTP', false, true, '587'),
('email.smtp_secure', 'true', 'boolean', 'email', 'Usar TLS para conexion SMTP', false, true, 'true'),
('email.from_address', '"noreply@erp-core.local"', 'string', 'email', 'Email de origen por defecto', true, true, '"noreply@erp-core.local"'),
('email.from_name', '"ERP Core"', 'string', 'email', 'Nombre de remitente por defecto', true, true, '"ERP Core"'),

-- Storage
('storage.max_file_size_mb', '10', 'number', 'storage', 'Tamano maximo de archivo en MB', true, true, '10'),
('storage.allowed_extensions', '["pdf","jpg","jpeg","png","xlsx","docx","csv"]', 'array', 'storage', 'Extensiones de archivo permitidas', true, true, '["pdf","jpg","jpeg","png","xlsx","docx","csv"]'),
('storage.provider', '"local"', 'string', 'storage', 'Proveedor de almacenamiento (local, s3, r2)', false, true, '"local"'),

-- Performance
('performance.cache_ttl_seconds', '300', 'number', 'performance', 'TTL de cache en segundos', false, true, '300'),
('performance.pagination_default_limit', '20', 'number', 'performance', 'Items por pagina por defecto', true, true, '20'),
('performance.pagination_max_limit', '100', 'number', 'performance', 'Maximo items por pagina', true, false, '100'),

-- Localization
('localization.default_timezone', '"America/Mexico_City"', 'string', 'localization', 'Timezone por defecto', true, true, '"America/Mexico_City"'),
('localization.default_locale', '"es-MX"', 'string', 'localization', 'Locale por defecto', true, true, '"es-MX"'),
('localization.default_currency', '"MXN"', 'string', 'localization', 'Moneda por defecto', true, true, '"MXN"'),
('localization.date_format', '"DD/MM/YYYY"', 'string', 'localization', 'Formato de fecha', true, true, '"DD/MM/YYYY"'),

-- Business
('business.fiscal_year_start_month', '1', 'number', 'business', 'Mes de inicio del ano fiscal', true, true, '1'),
('business.default_tax_rate', '16', 'number', 'business', 'Tasa de IVA por defecto', true, true, '16'),
('business.invoice_prefix', '"INV-"', 'string', 'business', 'Prefijo de facturas', true, true, '"INV-"'),
('business.require_approval_amount', '10000', 'number', 'business', 'Monto que requiere aprobacion', true, true, '10000')

ON CONFLICT (key) DO UPDATE SET
    value = EXCLUDED.value,
    updated_at = CURRENT_TIMESTAMP;

-- =====================
-- COMENTARIOS
-- =====================
COMMENT ON SCHEMA core_settings IS 'Schema para configuraciones del sistema multi-nivel';
COMMENT ON TABLE core_settings.system_settings IS 'Configuraciones globales del sistema';
COMMENT ON TABLE core_settings.plan_settings IS 'Configuraciones por defecto por plan de suscripcion';
COMMENT ON TABLE core_settings.tenant_settings IS 'Configuraciones personalizadas por tenant';
COMMENT ON TABLE core_settings.user_preferences IS 'Preferencias personales de usuario';
COMMENT ON FUNCTION core_settings.get_tenant_setting IS 'Obtiene setting efectivo con herencia system->plan->tenant';
COMMENT ON FUNCTION core_settings.get_user_preferences IS 'Obtiene todas las preferencias de un usuario';
COMMENT ON FUNCTION core_settings.get_all_tenant_settings IS 'Obtiene todos los settings de un tenant merged con sistema';
