-- =============================================================
-- ARCHIVO: 11-feature-flags.sql
-- DESCRIPCION: Sistema de Feature Flags para rollout gradual
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-01-10
-- EPIC: SAAS-BILLING (EPIC-SAAS-002)
-- HISTORIAS: US-022
-- =============================================================

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

-- =====================
-- TABLA: flags.flags
-- Definicion de feature flags globales (US-022)
-- =====================
CREATE TABLE IF NOT EXISTS flags.flags (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Identificacion
    key VARCHAR(100) UNIQUE NOT NULL,
    name VARCHAR(255) NOT NULL,
    description TEXT,
    category VARCHAR(100),

    -- Estado global
    enabled BOOLEAN DEFAULT FALSE,

    -- Rollout gradual
    rollout_percentage INTEGER DEFAULT 0 CHECK (rollout_percentage BETWEEN 0 AND 100),

    -- Targeting
    targeting_rules JSONB DEFAULT '[]',
    -- Ejemplo: [{"type": "tenant", "operator": "in", "values": ["uuid1", "uuid2"]}]

    -- Variantes (para A/B testing)
    variants JSONB DEFAULT '[]',
    -- Ejemplo: [{"key": "control", "weight": 50}, {"key": "variant_a", "weight": 50}]
    default_variant VARCHAR(100) DEFAULT 'control',

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

    -- Lifecycle
    starts_at TIMESTAMPTZ,
    ends_at TIMESTAMPTZ,

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

-- Indices para flags
CREATE INDEX IF NOT EXISTS idx_flags_key ON flags.flags(key);
CREATE INDEX IF NOT EXISTS idx_flags_enabled ON flags.flags(enabled) WHERE enabled = TRUE;
CREATE INDEX IF NOT EXISTS idx_flags_category ON flags.flags(category);
CREATE INDEX IF NOT EXISTS idx_flags_tags ON flags.flags USING GIN(tags);
CREATE INDEX IF NOT EXISTS idx_flags_active ON flags.flags(starts_at, ends_at)
    WHERE archived_at IS NULL;

-- =====================
-- TABLA: flags.flag_overrides
-- Overrides por tenant para feature flags (US-022)
-- =====================
CREATE TABLE IF NOT EXISTS flags.flag_overrides (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    flag_id UUID NOT NULL REFERENCES flags.flags(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Override
    enabled BOOLEAN NOT NULL,
    variant VARCHAR(100),  -- Variante especifica para este tenant

    -- Razon
    reason TEXT,
    expires_at TIMESTAMPTZ,

    -- Audit
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,

    UNIQUE(flag_id, tenant_id)
);

-- Indices para flag_overrides
CREATE INDEX IF NOT EXISTS idx_flag_overrides_flag ON flags.flag_overrides(flag_id);
CREATE INDEX IF NOT EXISTS idx_flag_overrides_tenant ON flags.flag_overrides(tenant_id);
CREATE INDEX IF NOT EXISTS idx_flag_overrides_active ON flags.flag_overrides(expires_at)
    WHERE expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP;

-- =====================
-- TABLA: flags.flag_evaluations
-- Log de evaluaciones de flags (para analytics)
-- =====================
CREATE TABLE IF NOT EXISTS flags.flag_evaluations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    flag_id UUID NOT NULL REFERENCES flags.flags(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    user_id UUID REFERENCES auth.users(id),

    -- Resultado
    result BOOLEAN NOT NULL,
    variant VARCHAR(100),

    -- Contexto de evaluacion
    evaluation_context JSONB DEFAULT '{}',
    evaluation_reason VARCHAR(100),  -- 'override', 'targeting', 'rollout', 'default'

    evaluated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Indices para flag_evaluations (particionado por fecha recomendado en produccion)
CREATE INDEX IF NOT EXISTS idx_flag_evaluations_flag ON flags.flag_evaluations(flag_id);
CREATE INDEX IF NOT EXISTS idx_flag_evaluations_tenant ON flags.flag_evaluations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_flag_evaluations_date ON flags.flag_evaluations(evaluated_at DESC);

-- =====================
-- TABLA: flags.flag_segments
-- Segmentos de usuarios para targeting avanzado
-- =====================
CREATE TABLE IF NOT EXISTS flags.flag_segments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Identificacion
    key VARCHAR(100) UNIQUE NOT NULL,
    name VARCHAR(255) NOT NULL,
    description TEXT,

    -- Reglas del segmento
    rules JSONB NOT NULL DEFAULT '[]',
    -- Ejemplo: [{"attribute": "plan", "operator": "eq", "value": "business"}]

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,

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

-- Indices para flag_segments
CREATE INDEX IF NOT EXISTS idx_flag_segments_key ON flags.flag_segments(key);
CREATE INDEX IF NOT EXISTS idx_flag_segments_active ON flags.flag_segments(is_active) WHERE is_active = TRUE;

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

-- Flags son globales, lectura publica
ALTER TABLE flags.flags ENABLE ROW LEVEL SECURITY;
CREATE POLICY public_read_flags ON flags.flags
    FOR SELECT USING (true);

-- Overrides son por tenant
ALTER TABLE flags.flag_overrides ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_overrides ON flags.flag_overrides
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Evaluations son por tenant
ALTER TABLE flags.flag_evaluations ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_evaluations ON flags.flag_evaluations
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Segments son globales
ALTER TABLE flags.flag_segments ENABLE ROW LEVEL SECURITY;
CREATE POLICY public_read_segments ON flags.flag_segments
    FOR SELECT USING (true);

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

-- Funcion principal para evaluar un flag
CREATE OR REPLACE FUNCTION flags.evaluate_flag(
    p_flag_key VARCHAR(100),
    p_tenant_id UUID,
    p_user_id UUID DEFAULT NULL,
    p_context JSONB DEFAULT '{}'
)
RETURNS TABLE (
    enabled BOOLEAN,
    variant VARCHAR(100),
    reason VARCHAR(100)
) AS $$
DECLARE
    v_flag RECORD;
    v_override RECORD;
    v_result BOOLEAN;
    v_variant VARCHAR(100);
    v_reason VARCHAR(100);
BEGIN
    -- Obtener flag
    SELECT * INTO v_flag
    FROM flags.flags
    WHERE key = p_flag_key
    AND archived_at IS NULL
    AND (starts_at IS NULL OR starts_at <= CURRENT_TIMESTAMP)
    AND (ends_at IS NULL OR ends_at > CURRENT_TIMESTAMP);

    IF NOT FOUND THEN
        RETURN QUERY SELECT FALSE, 'control'::VARCHAR(100), 'flag_not_found'::VARCHAR(100);
        RETURN;
    END IF;

    -- Verificar override para el tenant
    SELECT * INTO v_override
    FROM flags.flag_overrides
    WHERE flag_id = v_flag.id
    AND tenant_id = p_tenant_id
    AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP);

    IF FOUND THEN
        v_result := v_override.enabled;
        v_variant := COALESCE(v_override.variant, v_flag.default_variant);
        v_reason := 'override';
    ELSE
        -- Evaluar targeting rules (simplificado)
        IF v_flag.targeting_rules IS NOT NULL AND jsonb_array_length(v_flag.targeting_rules) > 0 THEN
            -- Por ahora, si hay targeting rules y el tenant esta en la lista, habilitar
            IF EXISTS (
                SELECT 1 FROM jsonb_array_elements(v_flag.targeting_rules) AS rule
                WHERE rule->>'type' = 'tenant'
                AND p_tenant_id::text = ANY(
                    SELECT jsonb_array_elements_text(rule->'values')
                )
            ) THEN
                v_result := TRUE;
                v_reason := 'targeting';
            END IF;
        END IF;

        -- Si no paso targeting, evaluar rollout
        IF v_reason IS NULL THEN
            IF v_flag.rollout_percentage > 0 THEN
                -- Usar hash del tenant_id para consistencia
                IF (abs(hashtext(p_tenant_id::text)) % 100) < v_flag.rollout_percentage THEN
                    v_result := TRUE;
                    v_reason := 'rollout';
                ELSE
                    v_result := v_flag.enabled;
                    v_reason := 'default';
                END IF;
            ELSE
                v_result := v_flag.enabled;
                v_reason := 'default';
            END IF;
        END IF;

        v_variant := v_flag.default_variant;
    END IF;

    -- Registrar evaluacion (async en produccion)
    INSERT INTO flags.flag_evaluations (flag_id, tenant_id, user_id, result, variant, evaluation_context, evaluation_reason)
    VALUES (v_flag.id, p_tenant_id, p_user_id, v_result, v_variant, p_context, v_reason);

    RETURN QUERY SELECT v_result, v_variant, v_reason;
END;
$$ LANGUAGE plpgsql;

-- Funcion simplificada para solo verificar si esta habilitado
CREATE OR REPLACE FUNCTION flags.is_enabled(
    p_flag_key VARCHAR(100),
    p_tenant_id UUID
)
RETURNS BOOLEAN AS $$
DECLARE
    v_enabled BOOLEAN;
BEGIN
    SELECT enabled INTO v_enabled
    FROM flags.evaluate_flag(p_flag_key, p_tenant_id);

    RETURN COALESCE(v_enabled, FALSE);
END;
$$ LANGUAGE plpgsql;

-- Funcion para obtener todos los flags de un tenant
CREATE OR REPLACE FUNCTION flags.get_all_flags_for_tenant(p_tenant_id UUID)
RETURNS TABLE (
    flag_key VARCHAR(100),
    flag_name VARCHAR(255),
    enabled BOOLEAN,
    variant VARCHAR(100)
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        f.key as flag_key,
        f.name as flag_name,
        COALESCE(fo.enabled, f.enabled) as enabled,
        COALESCE(fo.variant, f.default_variant) as variant
    FROM flags.flags f
    LEFT JOIN flags.flag_overrides fo ON fo.flag_id = f.id AND fo.tenant_id = p_tenant_id
    WHERE f.archived_at IS NULL
    AND (f.starts_at IS NULL OR f.starts_at <= CURRENT_TIMESTAMP)
    AND (f.ends_at IS NULL OR f.ends_at > CURRENT_TIMESTAMP);
END;
$$ LANGUAGE plpgsql STABLE;

-- Funcion para obtener estadisticas de un flag
CREATE OR REPLACE FUNCTION flags.get_flag_stats(
    p_flag_key VARCHAR(100),
    p_days INTEGER DEFAULT 7
)
RETURNS TABLE (
    total_evaluations BIGINT,
    enabled_count BIGINT,
    disabled_count BIGINT,
    enabled_percentage DECIMAL,
    unique_tenants BIGINT
) AS $$
BEGIN
    RETURN QUERY
    SELECT
        COUNT(*) as total_evaluations,
        COUNT(*) FILTER (WHERE fe.result = TRUE) as enabled_count,
        COUNT(*) FILTER (WHERE fe.result = FALSE) as disabled_count,
        ROUND(COUNT(*) FILTER (WHERE fe.result = TRUE)::DECIMAL / NULLIF(COUNT(*), 0) * 100, 2) as enabled_percentage,
        COUNT(DISTINCT fe.tenant_id) as unique_tenants
    FROM flags.flag_evaluations fe
    JOIN flags.flags f ON f.id = fe.flag_id
    WHERE f.key = p_flag_key
    AND fe.evaluated_at >= CURRENT_TIMESTAMP - (p_days || ' days')::INTERVAL;
END;
$$ LANGUAGE plpgsql STABLE;

-- Funcion para limpiar evaluaciones antiguas
CREATE OR REPLACE FUNCTION flags.cleanup_old_evaluations(p_days INTEGER DEFAULT 30)
RETURNS INTEGER AS $$
DECLARE
    deleted_count INTEGER;
BEGIN
    DELETE FROM flags.flag_evaluations
    WHERE evaluated_at < CURRENT_TIMESTAMP - (p_days || ' days')::INTERVAL;

    GET DIAGNOSTICS deleted_count = ROW_COUNT;
    RETURN deleted_count;
END;
$$ LANGUAGE plpgsql;

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

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

CREATE TRIGGER trg_flags_updated_at
    BEFORE UPDATE ON flags.flags
    FOR EACH ROW
    EXECUTE FUNCTION flags.update_timestamp();

CREATE TRIGGER trg_flag_overrides_updated_at
    BEFORE UPDATE ON flags.flag_overrides
    FOR EACH ROW
    EXECUTE FUNCTION flags.update_timestamp();

CREATE TRIGGER trg_flag_segments_updated_at
    BEFORE UPDATE ON flags.flag_segments
    FOR EACH ROW
    EXECUTE FUNCTION flags.update_timestamp();

-- =====================
-- SEED DATA: Feature Flags Base
-- =====================
INSERT INTO flags.flags (key, name, description, category, enabled, rollout_percentage, tags) VALUES
-- Features nuevas (deshabilitadas por default)
('new_dashboard', 'Nuevo Dashboard', 'Dashboard rediseñado con metricas en tiempo real', 'ui', FALSE, 0, '{ui,beta}'),
('ai_assistant', 'Asistente IA', 'Chat con asistente de inteligencia artificial', 'ai', FALSE, 0, '{ai,premium}'),
('whatsapp_notifications', 'Notificaciones WhatsApp', 'Enviar notificaciones via WhatsApp', 'notifications', FALSE, 0, '{notifications,beta}'),
('offline_mode', 'Modo Offline Mejorado', 'Sincronizacion offline avanzada', 'mobile', FALSE, 0, '{mobile,beta}'),
('multi_currency', 'Multi-Moneda', 'Soporte para multiples monedas', 'billing', FALSE, 0, '{billing,premium}'),

-- Features de rollout gradual
('new_checkout', 'Nuevo Flujo de Checkout', 'Checkout optimizado con menos pasos', 'billing', FALSE, 25, '{billing,ab_test}'),
('smart_inventory', 'Inventario Inteligente', 'Predicciones de stock con ML', 'inventory', FALSE, 10, '{inventory,ai,beta}'),

-- Features habilitadas globalmente
('dark_mode', 'Modo Oscuro', 'Tema oscuro para la interfaz', 'ui', TRUE, 100, '{ui}'),
('export_csv', 'Exportar a CSV', 'Exportar datos a formato CSV', 'reports', TRUE, 100, '{reports}'),
('notifications_center', 'Centro de Notificaciones', 'Panel centralizado de notificaciones', 'notifications', TRUE, 100, '{notifications}'),

-- Kill switches (para emergencias)
('maintenance_mode', 'Modo Mantenimiento', 'Mostrar pagina de mantenimiento', 'system', FALSE, 0, '{system,kill_switch}'),
('disable_signups', 'Deshabilitar Registros', 'Pausar nuevos registros', 'system', FALSE, 0, '{system,kill_switch}'),
('read_only_mode', 'Modo Solo Lectura', 'Deshabilitar escrituras en DB', 'system', FALSE, 0, '{system,kill_switch}')
ON CONFLICT (key) DO NOTHING;

-- =====================
-- SEED DATA: Segmentos
-- =====================
INSERT INTO flags.flag_segments (key, name, description, rules) VALUES
('beta_testers', 'Beta Testers', 'Usuarios en programa beta', '[{"attribute": "tags", "operator": "contains", "value": "beta"}]'),
('enterprise_customers', 'Clientes Enterprise', 'Tenants con plan enterprise', '[{"attribute": "plan", "operator": "eq", "value": "enterprise"}]'),
('high_usage', 'Alto Uso', 'Tenants con alto volumen de uso', '[{"attribute": "monthly_transactions", "operator": "gt", "value": 1000}]'),
('mexico_only', 'Solo Mexico', 'Tenants en Mexico', '[{"attribute": "country", "operator": "eq", "value": "MX"}]')
ON CONFLICT (key) DO NOTHING;

-- =====================
-- COMENTARIOS
-- =====================
COMMENT ON TABLE flags.flags IS 'Feature flags globales para control de funcionalidades';
COMMENT ON TABLE flags.flag_overrides IS 'Overrides de flags por tenant';
COMMENT ON TABLE flags.flag_evaluations IS 'Log de evaluaciones de flags para analytics';
COMMENT ON TABLE flags.flag_segments IS 'Segmentos de usuarios para targeting';

COMMENT ON FUNCTION flags.evaluate_flag IS 'Evalua un flag para un tenant, retorna enabled, variant y reason';
COMMENT ON FUNCTION flags.is_enabled IS 'Verifica si un flag esta habilitado para un tenant';
COMMENT ON FUNCTION flags.get_all_flags_for_tenant IS 'Obtiene todos los flags con su estado para un tenant';
COMMENT ON FUNCTION flags.get_flag_stats IS 'Obtiene estadisticas de uso de un flag';
