-- ============================================================================
-- SCHEMA: llm
-- TABLE: llm_proactive_notifications
-- DESCRIPTION: Notificaciones proactivas generadas por el agente LLM
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 5 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Enum para tipo de notificacion proactiva
DO $$ BEGIN
    CREATE TYPE llm.proactive_notification_type AS ENUM (
        'market_alert',         -- Alerta de mercado
        'trading_opportunity',  -- Oportunidad de trading
        'portfolio_update',     -- Actualizacion de portafolio
        'risk_warning',         -- Advertencia de riesgo
        'news_digest',          -- Resumen de noticias
        'learning_reminder',    -- Recordatorio de aprendizaje
        'goal_progress',        -- Progreso hacia metas
        'weekly_summary',       -- Resumen semanal
        'strategy_insight',     -- Insight de estrategia
        'market_open',          -- Apertura de mercado
        'market_close',         -- Cierre de mercado
        'custom'                -- Personalizado
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para prioridad
DO $$ BEGIN
    CREATE TYPE llm.notification_priority AS ENUM (
        'low',
        'normal',
        'high',
        'urgent'
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para estado de notificacion
DO $$ BEGIN
    CREATE TYPE llm.notification_status AS ENUM (
        'pending',              -- Pendiente de enviar
        'scheduled',            -- Programada
        'sent',                 -- Enviada
        'delivered',            -- Entregada
        'read',                 -- Leida
        'clicked',              -- Click en accion
        'dismissed',            -- Descartada
        'failed',               -- Fallo en envio
        'expired'               -- Expirada
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de Notificaciones Proactivas
CREATE TABLE IF NOT EXISTS llm.llm_proactive_notifications (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,

    -- Tipo y prioridad
    type llm.proactive_notification_type NOT NULL,
    priority llm.notification_priority NOT NULL DEFAULT 'normal',
    status llm.notification_status NOT NULL DEFAULT 'pending',

    -- Contenido
    title VARCHAR(255) NOT NULL,
    body TEXT NOT NULL,
    summary TEXT,                                       -- Resumen corto para push

    -- Accion
    action_type VARCHAR(50),                            -- 'open_chart', 'view_position', 'start_lesson'
    action_url TEXT,
    action_data JSONB DEFAULT '{}'::JSONB,

    -- Contexto
    context_type VARCHAR(50),                           -- 'symbol', 'position', 'course'
    context_id UUID,
    related_symbols VARCHAR(20)[],

    -- Canales de entrega
    channels VARCHAR(20)[] DEFAULT ARRAY['in_app'],     -- 'in_app', 'push', 'email', 'sms'
    channel_results JSONB DEFAULT '{}'::JSONB,          -- Resultado por canal

    -- Generacion
    generated_by VARCHAR(100),                          -- Modelo/servicio que genero
    generation_reason TEXT,
    generation_data JSONB DEFAULT '{}'::JSONB,          -- Datos usados para generar

    -- Confianza y relevancia
    confidence_score DECIMAL(5, 4),                     -- 0-1
    relevance_score DECIMAL(5, 4),                      -- 0-1
    personalization_score DECIMAL(5, 4),                -- 0-1

    -- Programacion
    scheduled_for TIMESTAMPTZ,
    send_window_start TIME,                             -- Ventana de envio preferida
    send_window_end TIME,
    timezone VARCHAR(50) DEFAULT 'UTC',

    -- Envio
    sent_at TIMESTAMPTZ,
    delivered_at TIMESTAMPTZ,
    read_at TIMESTAMPTZ,
    clicked_at TIMESTAMPTZ,
    dismissed_at TIMESTAMPTZ,

    -- Expiracion
    expires_at TIMESTAMPTZ,

    -- Feedback
    user_feedback VARCHAR(20),                          -- 'helpful', 'not_helpful', 'spam'
    feedback_text TEXT,
    feedback_at TIMESTAMPTZ,

    -- Agrupacion
    group_key VARCHAR(100),                             -- Para agrupar notificaciones similares
    is_grouped BOOLEAN NOT NULL DEFAULT FALSE,
    group_count INTEGER DEFAULT 1,

    -- Metadata
    tags VARCHAR(50)[],
    metadata JSONB DEFAULT '{}'::JSONB,

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

COMMENT ON TABLE llm.llm_proactive_notifications IS
'Notificaciones proactivas generadas por el agente LLM para engagement del usuario';

COMMENT ON COLUMN llm.llm_proactive_notifications.confidence_score IS
'Confianza del modelo en la relevancia de la notificacion';

-- Indices
CREATE INDEX IF NOT EXISTS idx_proactive_notif_tenant
    ON llm.llm_proactive_notifications(tenant_id);

CREATE INDEX IF NOT EXISTS idx_proactive_notif_user
    ON llm.llm_proactive_notifications(user_id);

CREATE INDEX IF NOT EXISTS idx_proactive_notif_status
    ON llm.llm_proactive_notifications(status);

CREATE INDEX IF NOT EXISTS idx_proactive_notif_pending
    ON llm.llm_proactive_notifications(user_id, status, scheduled_for)
    WHERE status IN ('pending', 'scheduled');

CREATE INDEX IF NOT EXISTS idx_proactive_notif_type
    ON llm.llm_proactive_notifications(type, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_proactive_notif_priority
    ON llm.llm_proactive_notifications(priority, status)
    WHERE priority IN ('high', 'urgent') AND status = 'pending';

CREATE INDEX IF NOT EXISTS idx_proactive_notif_scheduled
    ON llm.llm_proactive_notifications(scheduled_for)
    WHERE status = 'scheduled' AND scheduled_for IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_proactive_notif_unread
    ON llm.llm_proactive_notifications(user_id, read_at)
    WHERE status IN ('sent', 'delivered') AND read_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_proactive_notif_group
    ON llm.llm_proactive_notifications(group_key)
    WHERE group_key IS NOT NULL;

-- Trigger para updated_at
DROP TRIGGER IF EXISTS proactive_notif_updated_at ON llm.llm_proactive_notifications;
CREATE TRIGGER proactive_notif_updated_at
    BEFORE UPDATE ON llm.llm_proactive_notifications
    FOR EACH ROW
    EXECUTE FUNCTION llm.update_llm_timestamp();

-- Funcion para marcar como leida
CREATE OR REPLACE FUNCTION llm.mark_notification_read(
    p_notification_id UUID
)
RETURNS VOID AS $$
BEGIN
    UPDATE llm.llm_proactive_notifications
    SET status = 'read',
        read_at = NOW()
    WHERE id = p_notification_id
      AND read_at IS NULL;
END;
$$ LANGUAGE plpgsql;

-- Funcion para obtener notificaciones pendientes
CREATE OR REPLACE FUNCTION llm.get_pending_notifications(
    p_user_id UUID,
    p_limit INTEGER DEFAULT 20
)
RETURNS SETOF llm.llm_proactive_notifications AS $$
BEGIN
    RETURN QUERY
    SELECT *
    FROM llm.llm_proactive_notifications
    WHERE user_id = p_user_id
      AND status IN ('sent', 'delivered')
      AND read_at IS NULL
      AND (expires_at IS NULL OR expires_at > NOW())
    ORDER BY
        CASE priority
            WHEN 'urgent' THEN 1
            WHEN 'high' THEN 2
            WHEN 'normal' THEN 3
            ELSE 4
        END,
        created_at DESC
    LIMIT p_limit;
END;
$$ LANGUAGE plpgsql;

-- Funcion para contar no leidas
CREATE OR REPLACE FUNCTION llm.count_unread_notifications(p_user_id UUID)
RETURNS INTEGER AS $$
DECLARE
    v_count INTEGER;
BEGIN
    SELECT COUNT(*) INTO v_count
    FROM llm.llm_proactive_notifications
    WHERE user_id = p_user_id
      AND status IN ('sent', 'delivered')
      AND read_at IS NULL
      AND (expires_at IS NULL OR expires_at > NOW());

    RETURN v_count;
END;
$$ LANGUAGE plpgsql;

-- Vista de notificaciones para enviar
CREATE OR REPLACE VIEW llm.v_notifications_to_send AS
SELECT *
FROM llm.llm_proactive_notifications
WHERE status = 'scheduled'
  AND scheduled_for <= NOW()
  AND (expires_at IS NULL OR expires_at > NOW())
ORDER BY priority DESC, scheduled_for;

-- Vista de efectividad de notificaciones
CREATE OR REPLACE VIEW llm.v_notification_effectiveness AS
SELECT
    type,
    priority,
    COUNT(*) AS total_sent,
    COUNT(*) FILTER (WHERE read_at IS NOT NULL) AS read_count,
    COUNT(*) FILTER (WHERE clicked_at IS NOT NULL) AS clicked_count,
    ROUND((COUNT(*) FILTER (WHERE read_at IS NOT NULL)::DECIMAL
        / NULLIF(COUNT(*), 0) * 100), 2) AS read_rate,
    ROUND((COUNT(*) FILTER (WHERE clicked_at IS NOT NULL)::DECIMAL
        / NULLIF(COUNT(*) FILTER (WHERE read_at IS NOT NULL), 0) * 100), 2) AS click_rate,
    COUNT(*) FILTER (WHERE user_feedback = 'helpful') AS helpful_count,
    COUNT(*) FILTER (WHERE user_feedback = 'not_helpful') AS not_helpful_count
FROM llm.llm_proactive_notifications
WHERE status IN ('sent', 'delivered', 'read', 'clicked', 'dismissed')
GROUP BY type, priority
ORDER BY total_sent DESC;

-- RLS Policies
ALTER TABLE llm.llm_proactive_notifications ENABLE ROW LEVEL SECURITY;

CREATE POLICY proactive_notif_tenant ON llm.llm_proactive_notifications
    FOR ALL
    USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID);

CREATE POLICY proactive_notif_user ON llm.llm_proactive_notifications
    FOR ALL
    USING (user_id = current_setting('app.current_user_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON llm.llm_proactive_notifications TO trading_app;
GRANT SELECT ON llm.llm_proactive_notifications TO trading_readonly;
GRANT SELECT ON llm.v_notifications_to_send TO trading_app;
GRANT SELECT ON llm.v_notification_effectiveness TO trading_app;
GRANT EXECUTE ON FUNCTION llm.mark_notification_read TO trading_app;
GRANT EXECUTE ON FUNCTION llm.get_pending_notifications TO trading_app;
GRANT EXECUTE ON FUNCTION llm.count_unread_notifications TO trading_app;
