-- ============================================================================
-- SCHEMA: llm
-- TABLE: llm_tools_usage
-- DESCRIPTION: Registro de uso de herramientas por el agente LLM
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 5 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Enum para categoria de herramienta
DO $$ BEGIN
    CREATE TYPE llm.tool_category AS ENUM (
        'market_data',          -- Datos de mercado
        'trading',              -- Operaciones de trading
        'portfolio',            -- Gestion de portafolio
        'analysis',             -- Analisis tecnico/fundamental
        'charts',               -- Graficos
        'alerts',               -- Alertas
        'education',            -- Educacion
        'news',                 -- Noticias
        'calendar',             -- Calendario economico
        'calculations',         -- Calculos financieros
        'external_api',         -- APIs externas
        'system'                -- Sistema
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Enum para estado de ejecucion
DO $$ BEGIN
    CREATE TYPE llm.tool_execution_status AS ENUM (
        'pending',              -- Pendiente
        'executing',            -- Ejecutando
        'success',              -- Exitoso
        'error',                -- Error
        'timeout',              -- Timeout
        'cancelled',            -- Cancelado
        'rate_limited',         -- Rate limited
        'permission_denied'     -- Sin permisos
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de Uso de Herramientas
CREATE TABLE IF NOT EXISTS llm.llm_tools_usage (
    -- 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,

    -- Referencias
    conversation_id UUID REFERENCES llm.conversations(id) ON DELETE SET NULL,
    message_id UUID REFERENCES llm.conversation_messages(id) ON DELETE SET NULL,

    -- Herramienta
    tool_name VARCHAR(100) NOT NULL,
    tool_category llm.tool_category NOT NULL,
    tool_version VARCHAR(20),

    -- Ejecucion
    status llm.tool_execution_status NOT NULL DEFAULT 'pending',
    execution_time_ms INTEGER,

    -- Input/Output
    input_params JSONB NOT NULL DEFAULT '{}'::JSONB,
    output_result JSONB,
    output_size_bytes INTEGER,

    -- Error
    error_code VARCHAR(50),
    error_message TEXT,
    error_details JSONB,

    -- Contexto
    context_symbol VARCHAR(20),
    context_timeframe VARCHAR(10),
    context_data JSONB DEFAULT '{}'::JSONB,

    -- Rate limiting
    rate_limit_bucket VARCHAR(50),
    rate_limit_remaining INTEGER,

    -- Costo
    cost_units DECIMAL(10, 4) DEFAULT 0,                -- Unidades de costo (API calls, etc)
    cost_usd DECIMAL(10, 6) DEFAULT 0,                  -- Costo en USD

    -- Auditoria
    ip_address INET,
    user_agent TEXT,

    -- Metadata
    metadata JSONB DEFAULT '{}'::JSONB,

    -- Timestamps
    requested_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

COMMENT ON TABLE llm.llm_tools_usage IS
'Registro de todas las ejecuciones de herramientas por el agente LLM';

COMMENT ON COLUMN llm.llm_tools_usage.cost_units IS
'Unidades de costo internas (ej: 1 API call = 1 unit)';

-- Indices
CREATE INDEX IF NOT EXISTS idx_tools_usage_tenant
    ON llm.llm_tools_usage(tenant_id);

CREATE INDEX IF NOT EXISTS idx_tools_usage_user
    ON llm.llm_tools_usage(user_id);

CREATE INDEX IF NOT EXISTS idx_tools_usage_conversation
    ON llm.llm_tools_usage(conversation_id)
    WHERE conversation_id IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_tools_usage_tool
    ON llm.llm_tools_usage(tool_name, requested_at DESC);

CREATE INDEX IF NOT EXISTS idx_tools_usage_category
    ON llm.llm_tools_usage(tool_category, requested_at DESC);

CREATE INDEX IF NOT EXISTS idx_tools_usage_status
    ON llm.llm_tools_usage(status);

CREATE INDEX IF NOT EXISTS idx_tools_usage_errors
    ON llm.llm_tools_usage(status, tool_name)
    WHERE status IN ('error', 'timeout');

CREATE INDEX IF NOT EXISTS idx_tools_usage_date
    ON llm.llm_tools_usage(requested_at DESC);

-- Indice BRIN para datos temporales
CREATE INDEX IF NOT EXISTS idx_tools_usage_brin
    ON llm.llm_tools_usage USING BRIN (requested_at);

-- Funcion para registrar inicio de ejecucion
CREATE OR REPLACE FUNCTION llm.start_tool_execution(
    p_tool_usage_id UUID
)
RETURNS VOID AS $$
BEGIN
    UPDATE llm.llm_tools_usage
    SET status = 'executing',
        started_at = NOW()
    WHERE id = p_tool_usage_id;
END;
$$ LANGUAGE plpgsql;

-- Funcion para registrar fin exitoso
CREATE OR REPLACE FUNCTION llm.complete_tool_execution(
    p_tool_usage_id UUID,
    p_output JSONB
)
RETURNS VOID AS $$
DECLARE
    v_started_at TIMESTAMPTZ;
BEGIN
    SELECT started_at INTO v_started_at
    FROM llm.llm_tools_usage
    WHERE id = p_tool_usage_id;

    UPDATE llm.llm_tools_usage
    SET status = 'success',
        completed_at = NOW(),
        execution_time_ms = EXTRACT(EPOCH FROM (NOW() - v_started_at)) * 1000,
        output_result = p_output,
        output_size_bytes = LENGTH(p_output::TEXT)
    WHERE id = p_tool_usage_id;
END;
$$ LANGUAGE plpgsql;

-- Funcion para registrar error
CREATE OR REPLACE FUNCTION llm.fail_tool_execution(
    p_tool_usage_id UUID,
    p_error_code VARCHAR,
    p_error_message TEXT,
    p_error_details JSONB DEFAULT NULL
)
RETURNS VOID AS $$
BEGIN
    UPDATE llm.llm_tools_usage
    SET status = 'error',
        completed_at = NOW(),
        error_code = p_error_code,
        error_message = p_error_message,
        error_details = p_error_details
    WHERE id = p_tool_usage_id;
END;
$$ LANGUAGE plpgsql;

-- Vista de estadisticas de herramientas
CREATE OR REPLACE VIEW llm.v_tool_usage_stats AS
SELECT
    tool_name,
    tool_category,
    COUNT(*) AS total_calls,
    COUNT(*) FILTER (WHERE status = 'success') AS successful_calls,
    COUNT(*) FILTER (WHERE status = 'error') AS failed_calls,
    ROUND((COUNT(*) FILTER (WHERE status = 'success')::DECIMAL
        / NULLIF(COUNT(*), 0) * 100), 2) AS success_rate,
    ROUND(AVG(execution_time_ms)::NUMERIC, 2) AS avg_execution_ms,
    ROUND(PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY execution_time_ms)::NUMERIC, 2) AS p95_execution_ms,
    SUM(cost_units) AS total_cost_units,
    SUM(cost_usd) AS total_cost_usd
FROM llm.llm_tools_usage
WHERE completed_at IS NOT NULL
GROUP BY tool_name, tool_category
ORDER BY total_calls DESC;

-- Vista de errores recientes
CREATE OR REPLACE VIEW llm.v_tool_errors AS
SELECT
    id,
    user_id,
    tool_name,
    tool_category,
    error_code,
    error_message,
    input_params,
    requested_at
FROM llm.llm_tools_usage
WHERE status IN ('error', 'timeout')
ORDER BY requested_at DESC
LIMIT 100;

-- Vista de uso por usuario
CREATE OR REPLACE VIEW llm.v_user_tool_usage AS
SELECT
    user_id,
    tool_category,
    COUNT(*) AS total_calls,
    SUM(cost_units) AS total_cost_units,
    SUM(cost_usd) AS total_cost_usd,
    MAX(requested_at) AS last_used_at
FROM llm.llm_tools_usage
WHERE requested_at >= NOW() - INTERVAL '30 days'
GROUP BY user_id, tool_category
ORDER BY user_id, total_calls DESC;

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

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

CREATE POLICY tools_usage_user_isolation ON llm.llm_tools_usage
    FOR SELECT
    USING (user_id = current_setting('app.current_user_id', true)::UUID);

-- Grants
GRANT SELECT, INSERT, UPDATE ON llm.llm_tools_usage TO trading_app;
GRANT SELECT ON llm.llm_tools_usage TO trading_readonly;
GRANT SELECT ON llm.v_tool_usage_stats TO trading_app;
GRANT SELECT ON llm.v_tool_errors TO trading_app;
GRANT SELECT ON llm.v_user_tool_usage TO trading_app;
GRANT EXECUTE ON FUNCTION llm.start_tool_execution TO trading_app;
GRANT EXECUTE ON FUNCTION llm.complete_tool_execution TO trading_app;
GRANT EXECUTE ON FUNCTION llm.fail_tool_execution TO trading_app;
