CREATE OR REPLACE FUNCTION blob_to_clob(p_blob IN BLOB) RETURN CLOB IS
  l_clob      CLOB;
  l_varchar   VARCHAR2(32767);
  l_start     PLS_INTEGER := 1;
  l_buffer    PLS_INTEGER := 32767;
BEGIN
  DBMS_LOB.CREATETEMPORARY(l_clob, TRUE);
  
  FOR i IN 1 .. CEIL(DBMS_LOB.GETLENGTH(p_blob) / l_buffer) LOOP
    l_varchar := UTL_RAW.CAST_TO_VARCHAR2(
                   DBMS_LOB.SUBSTR(p_blob, l_buffer, l_start)
                 );
    DBMS_LOB.WRITEAPPEND(l_clob, LENGTH(l_varchar), l_varchar);
    l_start := l_start + l_buffer;
  END LOOP;
  
  RETURN l_clob;
END blob_to_clob;
/


CREATE OR REPLACE PROCEDURE CONVERT_MDFILES_TO_JSON AS
  l_md_content    CLOB;
  l_prompt        CLOB;
  l_final_json    CLOB;
  -- CHR(10) agrega saltos de linea al prompt.
BEGIN
  SELECT md_clob INTO l_md_content 
  FROM markdown_files WHERE STATUS = 'INGESTED'
  AND ROWNUM = 1
  FOR UPDATE;

  -- =====================================================================
  -- Llamada unica: metadatos de la promocion y lista de comercios
  -- =====================================================================
  l_prompt := 
    'Abaixo está um documento em markdown de uma promoção do ueno bank (Paraguai), em espanhol.' || CHR(10) ||
    'Extraia os metadados da promoção e TODOS os comércios em formato JSON estruturado.' || CHR(10) || CHR(10) ||
    'Retorne APENAS o objeto JSON abaixo (sem markdown fences, sem texto antes ou depois):' || CHR(10) ||
    '{' || CHR(10) ||
    '  "promocao": {' || CHR(10) ||
    '    "titulo": "...",' || CHR(10) ||
    '    "vigencia": {"data_inicio": "YYYY-MM-DD", "data_fim": "YYYY-MM-DD"}' || CHR(10) ||
    '  },' || CHR(10) ||
    '  "medios_pago": {' || CHR(10) ||
    '    "tarjetas_aceitas": ["lista completa"],' || CHR(10) ||
    '    "emissor": "...",' || CHR(10) ||
    '    "rede": "...",' || CHR(10) ||
    '    "billeteras_electronicas": ["lista"],' || CHR(10) ||
    '    "qr_permitido": true,' || CHR(10) ||
    '    "ecommerce_permitido": true' || CHR(10) ||
    '  },' || CHR(10) ||
    '  "beneficios_por_nivel": [' || CHR(10) ||
    '    {"nivel": "nivel 1", "percentual_reintegro": 0, "monto_compra_minimo_gs": 0, "reintegro_maximo_gs": 0}' || CHR(10) ||
    '  ],' || CHR(10) ||
    '  "cuotas": {' || CHR(10) ||
    '    "maximo_sin_intereses": 0,' || CHR(10) ||
    '    "requer_solicitacao_cliente": true' || CHR(10) ||
    '  },' || CHR(10) ||
    '  "acreditacao_reintegro": {' || CHR(10) ||
    '    "prazo_dias_habiles": 0,' || CHR(10) ||
    '    "percentual_creditado": 0,' || CHR(10) ||
    '    "tipo_conta": "..."' || CHR(10) ||
    '  },' || CHR(10) ||
    '  "exclusoes": ["frases curtas, 1 por item"],' || CHR(10) ||
    '  "comercios": [' || CHR(10) ||
    '    {"name": "...", "category": "...", "payment_processor": "upay"}' || CHR(10) ||
    '  ]' || CHR(10) ||
    '}' || CHR(10) || CHR(10) ||
    'REGRAS:' || CHR(10) ||
    '- Datas no formato ISO YYYY-MM-DD.' || CHR(10) ||
    '- Campos booleanos devem usar true ou false.' || CHR(10) ||
    '- Campos numericos devem usar numbers, sem aspas.' || CHR(10) ||
    '- Valores monetários em guaranies como integers (remover "Gs.", pontos e espaços).' || CHR(10) ||
    '- Se um campo não aparecer no documento, use null.' || CHR(10) ||
    '- exclusoes: liste apenas os principais pontos de exclusão em frases curtas.' || CHR(10) || CHR(10) ||
    'REGRAS PARA comercios:' || CHR(10) ||
    '- Extraia TODOS os comércios que aparecem literalmente no documento.' || CHR(10) ||
    '- category: seção em negrito mais próxima (**Belleza...**, **Grupo Zavidoro...**, etc).' || CHR(10) ||
    '- payment_processor: "upay" ou "bancard" conforme aparece ao lado do nome.' || CHR(10) ||
    '- Preserve os nomes exatamente como aparecem.' || CHR(10) ||
    '- Não invente comércios, categorias ou processadores de pagamento.' || CHR(10) || CHR(10) ||
    'DOCUMENTO:' || CHR(10) || l_md_content;

  l_final_json := DBMS_CLOUD_AI.GENERATE(
    prompt       => l_prompt,
    profile_name => 'PROFILE_GPT-5.5',
    action       => 'chat');


  DBMS_OUTPUT.PUT_LINE('Resposta bruta do LLM: ' || LENGTH(l_final_json) || ' chars');

  IF l_final_json IS NOT JSON THEN
    RAISE_APPLICATION_ERROR(-20210, 
      'LLM retornou JSON inválido. Últimos 200 chars: ' || SUBSTR(l_final_json, -200));
  END IF;
  
  -- Guarda el JSON estructurado generado por el modelo.
  INSERT INTO parsed_markdown_files (data) VALUES (l_final_json);

  UPDATE markdown_files SET STATUS = 'LOADED';
  COMMIT;

  DBMS_OUTPUT.PUT_LINE('JSON final: ' || LENGTH(l_final_json) || ' chars');
END;
/



set serveroutput on
-- drop tables
DECLARE
  l_count PLS_INTEGER := 0;
BEGIN
  -- Recorre solo tablas del esquema actual con el prefijo indicado.
  FOR t IN (
    SELECT table_name
    FROM user_tables
    WHERE table_name LIKE 'T\_%' ESCAPE '\'
    ORDER BY table_name
  ) LOOP
    -- Usa comillas dobles para soportar nombres creados con case-sensitive.
    EXECUTE IMMEDIATE
      'DROP TABLE "' || REPLACE(t.table_name, '"', '""') || '" CASCADE CONSTRAINTS PURGE';

    l_count := l_count + 1;
    DBMS_OUTPUT.PUT_LINE('Tabla eliminada: ' || t.table_name);
  END LOOP;

  DBMS_OUTPUT.PUT_LINE('Total de tablas eliminadas: ' || l_count);
END;
/