-- =============================================================
-- ARCHIVO: 14-ai.sql
-- DESCRIPCION: Sistema de AI/ML, prompts, completions, embeddings
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-01-10
-- EPIC: SAAS-AI (EPIC-SAAS-007)
-- HISTORIAS: US-080, US-081, US-082
-- =============================================================

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

-- =====================
-- EXTENSIÓN: pgvector para embeddings
-- =====================
-- CREATE EXTENSION IF NOT EXISTS vector;

-- =====================
-- TABLA: ai.models
-- Modelos de AI disponibles
-- =====================
CREATE TABLE IF NOT EXISTS ai.models (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),

    -- Identificación
    code VARCHAR(100) NOT NULL UNIQUE,
    name VARCHAR(200) NOT NULL,
    description TEXT,

    -- Proveedor
    provider VARCHAR(50) NOT NULL, -- openai, anthropic, google, azure, local
    model_id VARCHAR(100) NOT NULL, -- gpt-4, claude-3, etc.

    -- Tipo
    model_type VARCHAR(30) NOT NULL, -- chat, completion, embedding, image, audio

    -- Capacidades
    max_tokens INTEGER,
    supports_functions BOOLEAN DEFAULT FALSE,
    supports_vision BOOLEAN DEFAULT FALSE,
    supports_streaming BOOLEAN DEFAULT TRUE,

    -- Costos (por 1K tokens)
    input_cost_per_1k DECIMAL(10,6),
    output_cost_per_1k DECIMAL(10,6),

    -- Límites
    rate_limit_rpm INTEGER, -- Requests per minute
    rate_limit_tpm INTEGER, -- Tokens per minute

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,
    is_default BOOLEAN DEFAULT FALSE,

    -- Metadata
    metadata JSONB DEFAULT '{}',

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: ai.prompts
-- Biblioteca de prompts del sistema
-- =====================
CREATE TABLE IF NOT EXISTS ai.prompts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE, -- NULL = global

    -- Identificación
    code VARCHAR(100) NOT NULL,
    name VARCHAR(200) NOT NULL,
    description TEXT,
    category VARCHAR(50), -- assistant, analysis, generation, extraction

    -- Contenido
    system_prompt TEXT,
    user_prompt_template TEXT NOT NULL,
    -- Variables: {{variable_name}}

    -- Configuración del modelo
    model_id UUID REFERENCES ai.models(id),
    temperature DECIMAL(3,2) DEFAULT 0.7,
    max_tokens INTEGER,
    top_p DECIMAL(3,2),
    frequency_penalty DECIMAL(3,2),
    presence_penalty DECIMAL(3,2),

    -- Variables requeridas
    required_variables TEXT[] DEFAULT '{}',
    variable_schema JSONB DEFAULT '{}',

    -- Funciones (para function calling)
    functions JSONB DEFAULT '[]',

    -- Versionamiento
    version INTEGER DEFAULT 1,
    is_latest BOOLEAN DEFAULT TRUE,
    parent_version_id UUID REFERENCES ai.prompts(id),

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,
    is_system BOOLEAN DEFAULT FALSE,

    -- Estadísticas
    usage_count INTEGER DEFAULT 0,
    avg_tokens_used INTEGER,
    avg_latency_ms INTEGER,

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

    UNIQUE(tenant_id, code, version)
);

-- =====================
-- TABLA: ai.conversations
-- Conversaciones con el asistente AI
-- =====================
CREATE TABLE IF NOT EXISTS ai.conversations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,

    -- Identificación
    title VARCHAR(255),
    summary TEXT,

    -- Contexto
    context_type VARCHAR(50), -- general, sales, inventory, support
    context_data JSONB DEFAULT '{}',

    -- Modelo usado
    model_id UUID REFERENCES ai.models(id),
    prompt_id UUID REFERENCES ai.prompts(id),

    -- Estado
    status VARCHAR(20) DEFAULT 'active', -- active, archived, deleted
    is_pinned BOOLEAN DEFAULT FALSE,

    -- Estadísticas
    message_count INTEGER DEFAULT 0,
    total_tokens INTEGER DEFAULT 0,
    total_cost DECIMAL(10,4) DEFAULT 0,

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

    -- Tiempos
    last_message_at TIMESTAMPTZ,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: ai.messages
-- Mensajes en conversaciones
-- =====================
CREATE TABLE IF NOT EXISTS ai.messages (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    conversation_id UUID NOT NULL REFERENCES ai.conversations(id) ON DELETE CASCADE,
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Mensaje
    role VARCHAR(20) NOT NULL, -- system, user, assistant, function
    content TEXT NOT NULL,

    -- Función (si aplica)
    function_name VARCHAR(100),
    function_arguments JSONB,
    function_result JSONB,

    -- Modelo usado
    model_id UUID REFERENCES ai.models(id),
    model_response_id VARCHAR(255), -- ID de respuesta del proveedor

    -- Tokens y costos
    prompt_tokens INTEGER,
    completion_tokens INTEGER,
    total_tokens INTEGER,
    cost DECIMAL(10,6),

    -- Performance
    latency_ms INTEGER,
    finish_reason VARCHAR(30), -- stop, length, function_call, content_filter

    -- Metadata
    metadata JSONB DEFAULT '{}',

    -- Feedback
    feedback_rating INTEGER, -- 1-5
    feedback_text TEXT,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: ai.completions
-- Completaciones individuales (no conversacionales)
-- =====================
CREATE TABLE IF NOT EXISTS ai.completions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    user_id UUID REFERENCES auth.users(id),

    -- Prompt usado
    prompt_id UUID REFERENCES ai.prompts(id),
    prompt_code VARCHAR(100),

    -- Modelo
    model_id UUID REFERENCES ai.models(id),

    -- Input/Output
    input_text TEXT NOT NULL,
    input_variables JSONB DEFAULT '{}',
    output_text TEXT,

    -- Tokens y costos
    prompt_tokens INTEGER,
    completion_tokens INTEGER,
    total_tokens INTEGER,
    cost DECIMAL(10,6),

    -- Performance
    latency_ms INTEGER,
    finish_reason VARCHAR(30),

    -- Estado
    status VARCHAR(20) DEFAULT 'pending', -- pending, processing, completed, failed
    error_message TEXT,

    -- Contexto
    context_type VARCHAR(50),
    context_id UUID,

    -- Metadata
    metadata JSONB DEFAULT '{}',

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: ai.embeddings
-- Embeddings vectoriales
-- =====================
CREATE TABLE IF NOT EXISTS ai.embeddings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Contenido original
    content TEXT NOT NULL,
    content_hash VARCHAR(64), -- SHA-256 para deduplicación

    -- Vector
    -- embedding vector(1536), -- Para OpenAI ada-002
    embedding_json JSONB, -- Alternativa si no hay pgvector

    -- Modelo usado
    model_id UUID REFERENCES ai.models(id),
    model_name VARCHAR(100),
    dimensions INTEGER,

    -- Asociación
    entity_type VARCHAR(100), -- product, document, faq
    entity_id UUID,

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

    -- Chunks (si es parte de un documento grande)
    chunk_index INTEGER,
    chunk_total INTEGER,
    parent_embedding_id UUID REFERENCES ai.embeddings(id),

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: ai.usage_logs
-- Log de uso de AI para billing
-- =====================
CREATE TABLE IF NOT EXISTS ai.usage_logs (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
    user_id UUID REFERENCES auth.users(id),

    -- Modelo
    model_id UUID REFERENCES ai.models(id),
    model_name VARCHAR(100),
    provider VARCHAR(50),

    -- Tipo de uso
    usage_type VARCHAR(30) NOT NULL, -- chat, completion, embedding, image

    -- Tokens
    prompt_tokens INTEGER DEFAULT 0,
    completion_tokens INTEGER DEFAULT 0,
    total_tokens INTEGER DEFAULT 0,

    -- Costos
    cost DECIMAL(10,6) DEFAULT 0,

    -- Contexto
    conversation_id UUID,
    completion_id UUID,
    request_id VARCHAR(255),

    -- Periodo
    usage_date DATE DEFAULT CURRENT_DATE,
    usage_month VARCHAR(7), -- 2026-01

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- =====================
-- TABLA: ai.tenant_quotas
-- Cuotas de AI por tenant
-- =====================
CREATE TABLE IF NOT EXISTS ai.tenant_quotas (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Límites mensuales
    monthly_token_limit INTEGER,
    monthly_request_limit INTEGER,
    monthly_cost_limit DECIMAL(10,2),

    -- Uso actual del mes
    current_tokens INTEGER DEFAULT 0,
    current_requests INTEGER DEFAULT 0,
    current_cost DECIMAL(10,4) DEFAULT 0,

    -- Periodo
    quota_month VARCHAR(7) NOT NULL, -- 2026-01

    -- Estado
    is_exceeded BOOLEAN DEFAULT FALSE,
    exceeded_at TIMESTAMPTZ,

    -- Alertas
    alert_threshold_percent INTEGER DEFAULT 80,
    alert_sent_at TIMESTAMPTZ,

    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,

    UNIQUE(tenant_id, quota_month)
);

-- =====================
-- TABLA: ai.knowledge_base
-- Base de conocimiento para RAG
-- =====================
CREATE TABLE IF NOT EXISTS ai.knowledge_base (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE, -- NULL = global

    -- Identificación
    code VARCHAR(100) NOT NULL,
    name VARCHAR(200) NOT NULL,
    description TEXT,

    -- Fuente
    source_type VARCHAR(30), -- manual, document, website, api
    source_url TEXT,
    source_file_id UUID,

    -- Contenido
    content TEXT NOT NULL,
    content_type VARCHAR(50), -- faq, documentation, policy, procedure

    -- Categorización
    category VARCHAR(100),
    subcategory VARCHAR(100),
    tags TEXT[] DEFAULT '{}',

    -- Embedding
    embedding_id UUID REFERENCES ai.embeddings(id),

    -- Relevancia
    priority INTEGER DEFAULT 0,
    relevance_score DECIMAL(5,4),

    -- Estado
    is_active BOOLEAN DEFAULT TRUE,
    is_verified BOOLEAN DEFAULT FALSE,
    verified_by UUID REFERENCES auth.users(id),
    verified_at TIMESTAMPTZ,

    -- Metadata
    metadata JSONB DEFAULT '{}',

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

    UNIQUE(tenant_id, code)
);

-- =====================
-- INDICES
-- =====================

-- Indices para models
CREATE INDEX IF NOT EXISTS idx_models_provider ON ai.models(provider);
CREATE INDEX IF NOT EXISTS idx_models_type ON ai.models(model_type);
CREATE INDEX IF NOT EXISTS idx_models_active ON ai.models(is_active) WHERE is_active = TRUE;

-- Indices para prompts
CREATE INDEX IF NOT EXISTS idx_prompts_tenant ON ai.prompts(tenant_id);
CREATE INDEX IF NOT EXISTS idx_prompts_code ON ai.prompts(code);
CREATE INDEX IF NOT EXISTS idx_prompts_category ON ai.prompts(category);
CREATE INDEX IF NOT EXISTS idx_prompts_active ON ai.prompts(is_active) WHERE is_active = TRUE;

-- Indices para conversations
CREATE INDEX IF NOT EXISTS idx_conversations_tenant ON ai.conversations(tenant_id);
CREATE INDEX IF NOT EXISTS idx_conversations_user ON ai.conversations(user_id);
CREATE INDEX IF NOT EXISTS idx_conversations_status ON ai.conversations(status);
CREATE INDEX IF NOT EXISTS idx_conversations_created ON ai.conversations(created_at DESC);

-- Indices para messages
CREATE INDEX IF NOT EXISTS idx_messages_conversation ON ai.messages(conversation_id);
CREATE INDEX IF NOT EXISTS idx_messages_tenant ON ai.messages(tenant_id);
CREATE INDEX IF NOT EXISTS idx_messages_created ON ai.messages(created_at);

-- Indices para completions
CREATE INDEX IF NOT EXISTS idx_completions_tenant ON ai.completions(tenant_id);
CREATE INDEX IF NOT EXISTS idx_completions_user ON ai.completions(user_id);
CREATE INDEX IF NOT EXISTS idx_completions_prompt ON ai.completions(prompt_id);
CREATE INDEX IF NOT EXISTS idx_completions_context ON ai.completions(context_type, context_id);
CREATE INDEX IF NOT EXISTS idx_completions_created ON ai.completions(created_at DESC);

-- Indices para embeddings
CREATE INDEX IF NOT EXISTS idx_embeddings_tenant ON ai.embeddings(tenant_id);
CREATE INDEX IF NOT EXISTS idx_embeddings_entity ON ai.embeddings(entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_embeddings_hash ON ai.embeddings(content_hash);
CREATE INDEX IF NOT EXISTS idx_embeddings_tags ON ai.embeddings USING GIN(tags);

-- Indices para usage_logs
CREATE INDEX IF NOT EXISTS idx_usage_tenant ON ai.usage_logs(tenant_id);
CREATE INDEX IF NOT EXISTS idx_usage_date ON ai.usage_logs(usage_date);
CREATE INDEX IF NOT EXISTS idx_usage_month ON ai.usage_logs(usage_month);
CREATE INDEX IF NOT EXISTS idx_usage_model ON ai.usage_logs(model_id);

-- Indices para tenant_quotas
CREATE INDEX IF NOT EXISTS idx_quotas_tenant ON ai.tenant_quotas(tenant_id);
CREATE INDEX IF NOT EXISTS idx_quotas_month ON ai.tenant_quotas(quota_month);

-- Indices para knowledge_base
CREATE INDEX IF NOT EXISTS idx_kb_tenant ON ai.knowledge_base(tenant_id);
CREATE INDEX IF NOT EXISTS idx_kb_category ON ai.knowledge_base(category);
CREATE INDEX IF NOT EXISTS idx_kb_tags ON ai.knowledge_base USING GIN(tags);
CREATE INDEX IF NOT EXISTS idx_kb_active ON ai.knowledge_base(is_active) WHERE is_active = TRUE;

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

-- Models son globales
ALTER TABLE ai.models ENABLE ROW LEVEL SECURITY;
CREATE POLICY public_read_models ON ai.models
    FOR SELECT USING (is_active = TRUE);

-- Prompts: globales o por tenant
ALTER TABLE ai.prompts ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_or_global_prompts ON ai.prompts
    FOR SELECT USING (
        tenant_id IS NULL
        OR tenant_id = current_setting('app.current_tenant_id', true)::uuid
    );

-- Conversations por tenant
ALTER TABLE ai.conversations ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_conversations ON ai.conversations
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Messages por tenant
ALTER TABLE ai.messages ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_messages ON ai.messages
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Completions por tenant
ALTER TABLE ai.completions ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_completions ON ai.completions
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Embeddings por tenant
ALTER TABLE ai.embeddings ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_embeddings ON ai.embeddings
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Usage logs por tenant
ALTER TABLE ai.usage_logs ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_usage ON ai.usage_logs
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Quotas por tenant
ALTER TABLE ai.tenant_quotas ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_quotas ON ai.tenant_quotas
    USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Knowledge base: global o por tenant
ALTER TABLE ai.knowledge_base ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_or_global_kb ON ai.knowledge_base
    FOR SELECT USING (
        tenant_id IS NULL
        OR tenant_id = current_setting('app.current_tenant_id', true)::uuid
    );

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

-- Función para crear conversación
CREATE OR REPLACE FUNCTION ai.create_conversation(
    p_tenant_id UUID,
    p_user_id UUID,
    p_title VARCHAR(255) DEFAULT NULL,
    p_context_type VARCHAR(50) DEFAULT 'general',
    p_model_id UUID DEFAULT NULL
)
RETURNS UUID AS $$
DECLARE
    v_conversation_id UUID;
BEGIN
    INSERT INTO ai.conversations (
        tenant_id, user_id, title, context_type, model_id
    ) VALUES (
        p_tenant_id, p_user_id, p_title, p_context_type, p_model_id
    ) RETURNING id INTO v_conversation_id;

    RETURN v_conversation_id;
END;
$$ LANGUAGE plpgsql;

-- Función para agregar mensaje a conversación
CREATE OR REPLACE FUNCTION ai.add_message(
    p_conversation_id UUID,
    p_role VARCHAR(20),
    p_content TEXT,
    p_model_id UUID DEFAULT NULL,
    p_tokens JSONB DEFAULT NULL,
    p_latency_ms INTEGER DEFAULT NULL
)
RETURNS UUID AS $$
DECLARE
    v_message_id UUID;
    v_tenant_id UUID;
    v_prompt_tokens INTEGER;
    v_completion_tokens INTEGER;
    v_total_tokens INTEGER;
    v_cost DECIMAL(10,6);
BEGIN
    -- Obtener tenant de la conversación
    SELECT tenant_id INTO v_tenant_id
    FROM ai.conversations WHERE id = p_conversation_id;

    -- Extraer tokens
    v_prompt_tokens := (p_tokens->>'prompt_tokens')::INTEGER;
    v_completion_tokens := (p_tokens->>'completion_tokens')::INTEGER;
    v_total_tokens := COALESCE(v_prompt_tokens, 0) + COALESCE(v_completion_tokens, 0);

    -- Calcular costo (si hay modelo)
    IF p_model_id IS NOT NULL THEN
        SELECT
            (COALESCE(v_prompt_tokens, 0) * m.input_cost_per_1k / 1000) +
            (COALESCE(v_completion_tokens, 0) * m.output_cost_per_1k / 1000)
        INTO v_cost
        FROM ai.models m WHERE m.id = p_model_id;
    END IF;

    -- Crear mensaje
    INSERT INTO ai.messages (
        conversation_id, tenant_id, role, content, model_id,
        prompt_tokens, completion_tokens, total_tokens, cost, latency_ms
    ) VALUES (
        p_conversation_id, v_tenant_id, p_role, p_content, p_model_id,
        v_prompt_tokens, v_completion_tokens, v_total_tokens, v_cost, p_latency_ms
    ) RETURNING id INTO v_message_id;

    -- Actualizar estadísticas de conversación
    UPDATE ai.conversations
    SET
        message_count = message_count + 1,
        total_tokens = total_tokens + COALESCE(v_total_tokens, 0),
        total_cost = total_cost + COALESCE(v_cost, 0),
        last_message_at = CURRENT_TIMESTAMP,
        updated_at = CURRENT_TIMESTAMP
    WHERE id = p_conversation_id;

    -- Registrar uso
    IF v_total_tokens > 0 THEN
        PERFORM ai.log_usage(
            v_tenant_id, NULL, p_model_id, 'chat',
            v_prompt_tokens, v_completion_tokens, v_cost
        );
    END IF;

    RETURN v_message_id;
END;
$$ LANGUAGE plpgsql;

-- Función para registrar uso
CREATE OR REPLACE FUNCTION ai.log_usage(
    p_tenant_id UUID,
    p_user_id UUID,
    p_model_id UUID,
    p_usage_type VARCHAR(30),
    p_prompt_tokens INTEGER DEFAULT 0,
    p_completion_tokens INTEGER DEFAULT 0,
    p_cost DECIMAL(10,6) DEFAULT 0
)
RETURNS UUID AS $$
DECLARE
    v_log_id UUID;
    v_model_name VARCHAR(100);
    v_provider VARCHAR(50);
    v_current_month VARCHAR(7);
BEGIN
    -- Obtener info del modelo
    SELECT name, provider INTO v_model_name, v_provider
    FROM ai.models WHERE id = p_model_id;

    v_current_month := TO_CHAR(CURRENT_DATE, 'YYYY-MM');

    -- Registrar uso
    INSERT INTO ai.usage_logs (
        tenant_id, user_id, model_id, model_name, provider,
        usage_type, prompt_tokens, completion_tokens,
        total_tokens, cost, usage_month
    ) VALUES (
        p_tenant_id, p_user_id, p_model_id, v_model_name, v_provider,
        p_usage_type, p_prompt_tokens, p_completion_tokens,
        p_prompt_tokens + p_completion_tokens, p_cost, v_current_month
    ) RETURNING id INTO v_log_id;

    -- Actualizar cuota del tenant
    INSERT INTO ai.tenant_quotas (tenant_id, quota_month, current_tokens, current_requests, current_cost)
    VALUES (p_tenant_id, v_current_month, p_prompt_tokens + p_completion_tokens, 1, p_cost)
    ON CONFLICT (tenant_id, quota_month)
    DO UPDATE SET
        current_tokens = ai.tenant_quotas.current_tokens + p_prompt_tokens + p_completion_tokens,
        current_requests = ai.tenant_quotas.current_requests + 1,
        current_cost = ai.tenant_quotas.current_cost + p_cost,
        updated_at = CURRENT_TIMESTAMP;

    RETURN v_log_id;
END;
$$ LANGUAGE plpgsql;

-- Función para verificar cuota
CREATE OR REPLACE FUNCTION ai.check_quota(p_tenant_id UUID)
RETURNS TABLE (
    has_quota BOOLEAN,
    tokens_remaining INTEGER,
    requests_remaining INTEGER,
    cost_remaining DECIMAL,
    percent_used INTEGER
) AS $$
DECLARE
    v_quota RECORD;
BEGIN
    SELECT * INTO v_quota
    FROM ai.tenant_quotas
    WHERE tenant_id = p_tenant_id
    AND quota_month = TO_CHAR(CURRENT_DATE, 'YYYY-MM');

    IF NOT FOUND THEN
        -- Sin límites configurados
        RETURN QUERY SELECT TRUE, NULL::INTEGER, NULL::INTEGER, NULL::DECIMAL, 0;
        RETURN;
    END IF;

    RETURN QUERY SELECT
        NOT v_quota.is_exceeded AND
        (v_quota.monthly_token_limit IS NULL OR v_quota.current_tokens < v_quota.monthly_token_limit) AND
        (v_quota.monthly_request_limit IS NULL OR v_quota.current_requests < v_quota.monthly_request_limit) AND
        (v_quota.monthly_cost_limit IS NULL OR v_quota.current_cost < v_quota.monthly_cost_limit),

        CASE WHEN v_quota.monthly_token_limit IS NOT NULL
            THEN v_quota.monthly_token_limit - v_quota.current_tokens
            ELSE NULL END,

        CASE WHEN v_quota.monthly_request_limit IS NOT NULL
            THEN v_quota.monthly_request_limit - v_quota.current_requests
            ELSE NULL END,

        CASE WHEN v_quota.monthly_cost_limit IS NOT NULL
            THEN v_quota.monthly_cost_limit - v_quota.current_cost
            ELSE NULL END,

        CASE WHEN v_quota.monthly_token_limit IS NOT NULL
            THEN (v_quota.current_tokens * 100 / v_quota.monthly_token_limit)::INTEGER
            ELSE 0 END;
END;
$$ LANGUAGE plpgsql STABLE;

-- Función para obtener uso del tenant
CREATE OR REPLACE FUNCTION ai.get_tenant_usage(
    p_tenant_id UUID,
    p_month VARCHAR(7) DEFAULT NULL
)
RETURNS TABLE (
    total_tokens BIGINT,
    total_requests BIGINT,
    total_cost DECIMAL,
    usage_by_model JSONB,
    usage_by_type JSONB,
    daily_usage JSONB
) AS $$
DECLARE
    v_month VARCHAR(7);
BEGIN
    v_month := COALESCE(p_month, TO_CHAR(CURRENT_DATE, 'YYYY-MM'));

    RETURN QUERY
    SELECT
        COALESCE(SUM(ul.total_tokens), 0)::BIGINT as total_tokens,
        COUNT(*)::BIGINT as total_requests,
        COALESCE(SUM(ul.cost), 0)::DECIMAL as total_cost,
        jsonb_object_agg(
            COALESCE(ul.model_name, 'unknown'),
            model_tokens
        ) as usage_by_model,
        jsonb_object_agg(ul.usage_type, type_tokens) as usage_by_type,
        jsonb_object_agg(ul.usage_date::TEXT, day_tokens) as daily_usage
    FROM ai.usage_logs ul
    LEFT JOIN (
        SELECT model_name, SUM(total_tokens) as model_tokens
        FROM ai.usage_logs
        WHERE tenant_id = p_tenant_id AND usage_month = v_month
        GROUP BY model_name
    ) models ON models.model_name = ul.model_name
    LEFT JOIN (
        SELECT usage_type, SUM(total_tokens) as type_tokens
        FROM ai.usage_logs
        WHERE tenant_id = p_tenant_id AND usage_month = v_month
        GROUP BY usage_type
    ) types ON types.usage_type = ul.usage_type
    LEFT JOIN (
        SELECT usage_date, SUM(total_tokens) as day_tokens
        FROM ai.usage_logs
        WHERE tenant_id = p_tenant_id AND usage_month = v_month
        GROUP BY usage_date
    ) days ON days.usage_date = ul.usage_date
    WHERE ul.tenant_id = p_tenant_id
    AND ul.usage_month = v_month;
END;
$$ LANGUAGE plpgsql STABLE;

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

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

CREATE TRIGGER trg_models_updated_at
    BEFORE UPDATE ON ai.models
    FOR EACH ROW EXECUTE FUNCTION ai.update_timestamp();

CREATE TRIGGER trg_prompts_updated_at
    BEFORE UPDATE ON ai.prompts
    FOR EACH ROW EXECUTE FUNCTION ai.update_timestamp();

CREATE TRIGGER trg_conversations_updated_at
    BEFORE UPDATE ON ai.conversations
    FOR EACH ROW EXECUTE FUNCTION ai.update_timestamp();

CREATE TRIGGER trg_embeddings_updated_at
    BEFORE UPDATE ON ai.embeddings
    FOR EACH ROW EXECUTE FUNCTION ai.update_timestamp();

CREATE TRIGGER trg_kb_updated_at
    BEFORE UPDATE ON ai.knowledge_base
    FOR EACH ROW EXECUTE FUNCTION ai.update_timestamp();

-- =====================
-- SEED DATA: Modelos
-- =====================
INSERT INTO ai.models (code, name, provider, model_id, model_type, max_tokens, supports_functions, supports_vision, input_cost_per_1k, output_cost_per_1k) VALUES
('gpt-4o', 'GPT-4o', 'openai', 'gpt-4o', 'chat', 128000, TRUE, TRUE, 0.005, 0.015),
('gpt-4o-mini', 'GPT-4o Mini', 'openai', 'gpt-4o-mini', 'chat', 128000, TRUE, TRUE, 0.00015, 0.0006),
('gpt-4-turbo', 'GPT-4 Turbo', 'openai', 'gpt-4-turbo', 'chat', 128000, TRUE, TRUE, 0.01, 0.03),
('claude-3-opus', 'Claude 3 Opus', 'anthropic', 'claude-3-opus-20240229', 'chat', 200000, TRUE, TRUE, 0.015, 0.075),
('claude-3-sonnet', 'Claude 3 Sonnet', 'anthropic', 'claude-3-sonnet-20240229', 'chat', 200000, TRUE, TRUE, 0.003, 0.015),
('claude-3-haiku', 'Claude 3 Haiku', 'anthropic', 'claude-3-haiku-20240307', 'chat', 200000, TRUE, TRUE, 0.00025, 0.00125),
('text-embedding-3-small', 'Text Embedding 3 Small', 'openai', 'text-embedding-3-small', 'embedding', 8191, FALSE, FALSE, 0.00002, 0),
('text-embedding-3-large', 'Text Embedding 3 Large', 'openai', 'text-embedding-3-large', 'embedding', 8191, FALSE, FALSE, 0.00013, 0)
ON CONFLICT (code) DO NOTHING;

-- =====================
-- SEED DATA: Prompts del Sistema
-- =====================
INSERT INTO ai.prompts (code, name, category, system_prompt, user_prompt_template, required_variables, is_system) VALUES
('assistant_general', 'Asistente General', 'assistant',
 'Eres un asistente virtual para un sistema ERP. Ayudas a los usuarios con consultas sobre ventas, inventario, facturación y gestión empresarial. Responde de forma clara y concisa en español.',
 '{{user_message}}',
 '{user_message}', TRUE),

('sales_analysis', 'Análisis de Ventas', 'analysis',
 'Eres un analista de ventas experto. Analiza los datos proporcionados y genera insights accionables.',
 'Analiza los siguientes datos de ventas:\n\n{{sales_data}}\n\nGenera un resumen ejecutivo con los principales hallazgos.',
 '{sales_data}', TRUE),

('product_description', 'Generador de Descripción', 'generation',
 'Eres un copywriter experto en productos. Genera descripciones atractivas y persuasivas.',
 'Genera una descripción de producto para:\n\nNombre: {{product_name}}\nCategoría: {{category}}\nCaracterísticas: {{features}}\n\nLa descripción debe ser de {{word_count}} palabras aproximadamente.',
 '{product_name,category,features,word_count}', TRUE),

('invoice_data_extraction', 'Extracción de Facturas', 'extraction',
 'Eres un experto en extracción de datos de documentos fiscales mexicanos. Extrae la información estructurada de facturas.',
 'Extrae los datos de la siguiente factura:\n\n{{invoice_text}}\n\nDevuelve los datos en formato JSON con los campos: rfc_emisor, rfc_receptor, fecha, total, conceptos.',
 '{invoice_text}', TRUE),

('support_response', 'Respuesta de Soporte', 'assistant',
 'Eres un agente de soporte técnico. Responde de forma amable y profesional, proporcionando soluciones claras.',
 'El cliente tiene el siguiente problema:\n\n{{issue_description}}\n\nContexto adicional:\n{{context}}\n\nGenera una respuesta de soporte apropiada.',
 '{issue_description,context}', TRUE)

ON CONFLICT (tenant_id, code, version) DO NOTHING;

-- =====================
-- COMENTARIOS
-- =====================
COMMENT ON TABLE ai.models IS 'Modelos de AI disponibles (OpenAI, Anthropic, etc.)';
COMMENT ON TABLE ai.prompts IS 'Biblioteca de prompts del sistema y personalizados';
COMMENT ON TABLE ai.conversations IS 'Conversaciones con el asistente AI';
COMMENT ON TABLE ai.messages IS 'Mensajes individuales en conversaciones';
COMMENT ON TABLE ai.completions IS 'Completaciones individuales (no conversacionales)';
COMMENT ON TABLE ai.embeddings IS 'Embeddings vectoriales para búsqueda semántica';
COMMENT ON TABLE ai.usage_logs IS 'Log de uso de AI para billing y analytics';
COMMENT ON TABLE ai.tenant_quotas IS 'Cuotas de uso de AI por tenant';
COMMENT ON TABLE ai.knowledge_base IS 'Base de conocimiento para RAG';

COMMENT ON FUNCTION ai.create_conversation IS 'Crea una nueva conversación con el asistente';
COMMENT ON FUNCTION ai.add_message IS 'Agrega un mensaje a una conversación';
COMMENT ON FUNCTION ai.log_usage IS 'Registra uso de AI para billing';
COMMENT ON FUNCTION ai.check_quota IS 'Verifica si el tenant tiene cuota disponible';
