# DDL-SPEC: Schema core_settings

## Identificacion

| Campo | Valor |
|-------|-------|
| **Schema** | core_settings |
| **Modulo** | MGN-006 |
| **Version** | 1.0 |
| **Estado** | En Diseno |
| **Autor** | Requirements-Analyst |
| **Fecha** | 2025-12-05 |

---

## Descripcion General

El schema `core_settings` gestiona configuraciones del sistema, por tenant, preferencias de usuario y feature flags. Implementa herencia de configuraciones y evaluacion eficiente de flags.

### RF Cubiertos

| RF | Titulo | Tablas |
|----|--------|--------|
| RF-SETTINGS-001 | Configuraciones Sistema | system_settings |
| RF-SETTINGS-002 | Configuraciones Tenant | tenant_settings, plan_settings |
| RF-SETTINGS-003 | Preferencias Usuario | user_preferences |
| RF-SETTINGS-004 | Feature Flags | feature_flags, feature_flag_overrides |

---

## Diagrama ER

```mermaid
erDiagram
    system_settings {
        uuid id PK
        varchar key UK
        jsonb value
        varchar data_type
        varchar category
        text description
        boolean is_public
        boolean is_editable
        jsonb default_value
        jsonb validation_rules
    }

    plan_settings {
        uuid id PK
        uuid plan_id FK
        varchar key
        jsonb value
    }

    tenant_settings {
        uuid id PK
        uuid tenant_id FK
        varchar key
        jsonb value
        varchar inherited_from
        boolean is_overridden
    }

    user_preferences {
        uuid id PK
        uuid user_id FK
        varchar key
        jsonb value
        timestamptz synced_at
    }

    feature_flags {
        uuid id PK
        varchar key UK
        varchar name
        text description
        varchar flag_type
        jsonb default_value
        jsonb rollout_config
        boolean is_active
        timestamptz expires_at
    }

    feature_flag_overrides {
        uuid id PK
        uuid feature_flag_id FK
        varchar level
        uuid level_id
        jsonb value
        boolean is_active
    }

    plan_settings }o--|| subscription_plans : "plan"
    tenant_settings }o--|| tenants : "tenant"
    user_preferences }o--|| users : "user"
    feature_flag_overrides }o--|| feature_flags : "flag"
```

---

## Tablas

### 1. system_settings

Configuraciones globales del sistema.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `key` | VARCHAR(100) | NOT NULL | - | Clave unica |
| `value` | JSONB | NOT NULL | - | Valor actual |
| `data_type` | VARCHAR(20) | NOT NULL | 'string' | Tipo de dato |
| `category` | VARCHAR(50) | NOT NULL | - | Categoria |
| `description` | TEXT | NULL | - | Descripcion UI |
| `is_public` | BOOLEAN | NOT NULL | false | Visible a tenants |
| `is_editable` | BOOLEAN | NOT NULL | true | Modificable runtime |
| `default_value` | JSONB | NULL | - | Valor por defecto |
| `validation_rules` | JSONB | NULL | '{}' | Reglas validacion |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |
| `updated_by` | UUID | NULL | - | Usuario modificador |

```sql
CONSTRAINT pk_system_settings PRIMARY KEY (id),
CONSTRAINT uk_system_settings_key UNIQUE (key),
CONSTRAINT chk_system_settings_data_type
    CHECK (data_type IN ('string', 'number', 'boolean', 'json', 'array', 'secret'))
```

```sql
CREATE INDEX idx_system_settings_category ON core_settings.system_settings(category);
CREATE INDEX idx_system_settings_public ON core_settings.system_settings(is_public) WHERE is_public = true;
```

---

### 2. plan_settings

Configuraciones por defecto por plan de suscripcion.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `plan_id` | UUID | NOT NULL | - | FK a subscription_plans |
| `key` | VARCHAR(100) | NOT NULL | - | Clave de setting |
| `value` | JSONB | NOT NULL | - | Valor para el plan |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |

```sql
CONSTRAINT pk_plan_settings PRIMARY KEY (id),
CONSTRAINT uk_plan_settings_plan_key UNIQUE (plan_id, key),
CONSTRAINT fk_plan_settings_plan
    FOREIGN KEY (plan_id) REFERENCES core_tenants.subscription_plans(id) ON DELETE CASCADE
```

---

### 3. tenant_settings

Configuraciones personalizadas por tenant.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `tenant_id` | UUID | NOT NULL | - | FK a tenants |
| `key` | VARCHAR(100) | NOT NULL | - | Clave de setting |
| `value` | JSONB | NOT NULL | - | Valor personalizado |
| `inherited_from` | VARCHAR(20) | NOT NULL | 'custom' | system/plan/custom |
| `is_overridden` | BOOLEAN | NOT NULL | true | Sobreescribe herencia |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |

```sql
CONSTRAINT pk_tenant_settings PRIMARY KEY (id),
CONSTRAINT uk_tenant_settings_tenant_key UNIQUE (tenant_id, key),
CONSTRAINT fk_tenant_settings_tenant
    FOREIGN KEY (tenant_id) REFERENCES core_tenants.tenants(id) ON DELETE CASCADE,
CONSTRAINT chk_tenant_settings_inherited
    CHECK (inherited_from IN ('system', 'plan', 'custom'))
```

```sql
-- RLS
ALTER TABLE core_settings.tenant_settings ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON core_settings.tenant_settings
    FOR ALL USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
```

---

### 4. user_preferences

Preferencias personales de usuario.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `user_id` | UUID | NOT NULL | - | FK a users |
| `key` | VARCHAR(100) | NOT NULL | - | Clave de preferencia |
| `value` | JSONB | NOT NULL | - | Valor |
| `synced_at` | TIMESTAMPTZ | NOT NULL | NOW() | Ultima sync |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |

```sql
CONSTRAINT pk_user_preferences PRIMARY KEY (id),
CONSTRAINT uk_user_preferences_user_key UNIQUE (user_id, key),
CONSTRAINT fk_user_preferences_user
    FOREIGN KEY (user_id) REFERENCES core_users.users(id) ON DELETE CASCADE
```

---

### 5. feature_flags

Definicion de feature flags.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `key` | VARCHAR(100) | NOT NULL | - | Clave unica |
| `name` | VARCHAR(255) | NOT NULL | - | Nombre descriptivo |
| `description` | TEXT | NULL | - | Descripcion |
| `flag_type` | VARCHAR(20) | NOT NULL | 'boolean' | boolean/percentage/variant |
| `default_value` | JSONB | NOT NULL | 'false' | Valor por defecto |
| `rollout_config` | JSONB | NULL | '{}' | Config de rollout |
| `is_active` | BOOLEAN | NOT NULL | true | Flag activo |
| `expires_at` | TIMESTAMPTZ | NULL | - | Expiracion |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `updated_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha actualizacion |
| `created_by` | UUID | NULL | - | Usuario creador |

```sql
CONSTRAINT pk_feature_flags PRIMARY KEY (id),
CONSTRAINT uk_feature_flags_key UNIQUE (key),
CONSTRAINT chk_feature_flags_type
    CHECK (flag_type IN ('boolean', 'percentage', 'variant'))
```

---

### 6. feature_flag_overrides

Overrides de flags por nivel.

| Columna | Tipo | Nullable | Default | Descripcion |
|---------|------|----------|---------|-------------|
| `id` | UUID | NOT NULL | gen_random_uuid() | PK |
| `feature_flag_id` | UUID | NOT NULL | - | FK a feature_flags |
| `level` | VARCHAR(20) | NOT NULL | - | plan/tenant/user |
| `level_id` | UUID | NOT NULL | - | ID del nivel |
| `value` | JSONB | NOT NULL | - | Valor override |
| `is_active` | BOOLEAN | NOT NULL | true | Override activo |
| `created_at` | TIMESTAMPTZ | NOT NULL | NOW() | Fecha creacion |
| `created_by` | UUID | NULL | - | Usuario creador |

```sql
CONSTRAINT pk_feature_flag_overrides PRIMARY KEY (id),
CONSTRAINT uk_feature_flag_overrides UNIQUE (feature_flag_id, level, level_id),
CONSTRAINT fk_feature_flag_overrides_flag
    FOREIGN KEY (feature_flag_id) REFERENCES core_settings.feature_flags(id) ON DELETE CASCADE,
CONSTRAINT chk_feature_flag_overrides_level
    CHECK (level IN ('plan', 'tenant', 'user'))
```

```sql
CREATE INDEX idx_feature_flag_overrides_lookup
    ON core_settings.feature_flag_overrides(feature_flag_id, level, level_id);
```

---

## Funciones de Utilidad

### Obtener Setting Efectivo del Tenant

```sql
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
    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 core_tenants.tenants t WHERE t.id = p_tenant_id;

    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;

    -- 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;
```

### Evaluar Feature Flag

```sql
CREATE OR REPLACE FUNCTION core_settings.evaluate_feature_flag(
    p_key VARCHAR,
    p_tenant_id UUID DEFAULT NULL,
    p_user_id UUID DEFAULT NULL
) RETURNS JSONB AS $$
DECLARE
    v_flag RECORD;
    v_override JSONB;
    v_result JSONB;
BEGIN
    -- Obtener flag base
    SELECT * INTO v_flag
    FROM core_settings.feature_flags
    WHERE key = p_key AND is_active = true
    AND (expires_at IS NULL OR expires_at > NOW());

    IF v_flag IS NULL THEN
        RETURN jsonb_build_object('enabled', false, 'source', 'not_found');
    END IF;

    -- Buscar override por user
    IF p_user_id IS NOT NULL THEN
        SELECT value INTO v_override
        FROM core_settings.feature_flag_overrides
        WHERE feature_flag_id = v_flag.id AND level = 'user' AND level_id = p_user_id AND is_active = true;

        IF v_override IS NOT NULL THEN
            RETURN jsonb_build_object('enabled', v_override, 'source', 'user_override');
        END IF;
    END IF;

    -- Buscar override por tenant
    IF p_tenant_id IS NOT NULL THEN
        SELECT value INTO v_override
        FROM core_settings.feature_flag_overrides
        WHERE feature_flag_id = v_flag.id AND level = 'tenant' AND level_id = p_tenant_id AND is_active = true;

        IF v_override IS NOT NULL THEN
            RETURN jsonb_build_object('enabled', v_override, 'source', 'tenant_override');
        END IF;
    END IF;

    -- Retornar default
    RETURN jsonb_build_object('enabled', v_flag.default_value, 'source', 'default');
END;
$$ LANGUAGE plpgsql STABLE;
```

---

## Seed Data

### System Settings

```sql
INSERT INTO core_settings.system_settings (key, value, data_type, category, description, is_editable) VALUES
('security.max_login_attempts', '5', 'number', 'security', 'Intentos maximos de login', true),
('security.lockout_duration_minutes', '15', 'number', 'security', 'Duracion bloqueo en minutos', true),
('security.token_expiry_hours', '24', 'number', 'security', 'Expiracion token en horas', true),
('security.password_min_length', '8', 'number', 'security', 'Longitud minima password', true),
('email.smtp_host', '""', 'string', 'email', 'Host SMTP', true),
('email.smtp_port', '587', 'number', 'email', 'Puerto SMTP', true),
('storage.max_file_size_mb', '10', 'number', 'storage', 'Tamano maximo archivo MB', true),
('storage.allowed_extensions', '["pdf","jpg","png","xlsx"]', 'array', 'storage', 'Extensiones permitidas', true),
('performance.cache_ttl_seconds', '300', 'number', 'performance', 'TTL cache segundos', true),
('performance.pagination_max_limit', '100', 'number', 'performance', 'Max items por pagina', false);
```

### Feature Flags

```sql
INSERT INTO core_settings.feature_flags (key, name, flag_type, default_value, description) VALUES
('feature.oauth_login', 'Login con OAuth', 'boolean', 'true', 'Habilita login con proveedores OAuth'),
('feature.dark_mode', 'Modo Oscuro', 'boolean', 'true', 'Habilita tema oscuro'),
('feature.export_excel', 'Exportar Excel', 'boolean', 'true', 'Permite exportar a Excel'),
('feature.bulk_operations', 'Operaciones Masivas', 'boolean', 'true', 'Permite operaciones en lote'),
('feature.ai_suggestions', 'Sugerencias IA', 'boolean', 'false', 'Sugerencias con IA');
```

---

## Historial

| Version | Fecha | Autor | Cambios |
|---------|-------|-------|---------|
| 1.0 | 2025-12-05 | Requirements-Analyst | Creacion inicial |
