<?php
// public/api/agent_atendimento.php
// VERSÃO COM CRUD DE PROMPTS
// Mantém TUDO que já existia + adiciona:
//   - models_list, models_save, models_use, models_delete,
//     models_update, models_rename, models_import, models_export,
//     agent_autosave, models_migrate_now
//   - Blindagem safeBegin() contra conexão pooled "suja" do Supabase
//   - TEMPO_SET_DEFINITIVO corrigido (2bi pra caber em INTEGER)
require_once __DIR__ . '/../../app/config.php';
require_once __DIR__ . '/../../app/db.php';
require_once __DIR__ . '/../../app/csrf.php';

header('Content-Type: application/json; charset=utf-8');

function jexit(array $p, int $s=200){ http_response_code($s); echo json_encode($p); exit; }

/**
 * Abre uma transação garantindo que a conexão NÃO está com uma
 * transação pendente (vazada de outra request via pooler). Sem isso,
 * beginTransaction() sobre estado sujo dispara SQLSTATE[25P02].
 */
function safeBegin(PDO $pdo): void {
  if ($pdo->inTransaction()) {
    try { $pdo->rollBack(); }
    catch (Throwable $e) {
      try { $pdo->exec('ROLLBACK'); } catch (Throwable $ignore) {}
    }
  }
  $pdo->beginTransaction();
}

if (empty($_SESSION['user']['email'])) {
  jexit(['ok'=>false,'error'=>'não autenticado'], 401);
}
$EMAIL = $_SESSION['user']['email'];

$pdo = db();
$action = $_GET['action'] ?? 'get';

// === Blindagem contra conexão pooled "suja" ===
// O pooler do Supabase reaproveita conexões. Se uma request anterior
// deixou uma transação aberta ou ABORTADA, esta nova request herda
// esse estado e qualquer comando estoura SQLSTATE[25P02]. Então,
// ANTES de tudo, garantimos que a conexão começa limpa.
try {
  if ($pdo instanceof PDO && $pdo->inTransaction()) {
    $pdo->rollBack();
  }
} catch (Throwable $e) {
  try { $pdo->exec('ROLLBACK'); } catch (Throwable $ignore) {}
}

// Valor sentinela de "pausa definitiva".
// IMPORTANTE: a coluna prompt_models.tempo_de_set é INTEGER (limite ~2.147bi).
// O valor antigo 999.999.999.999 (12 dígitos) estourava o tipo na hora
// de inserir/atualizar e causava SQLSTATE[22003].
// 2.000.000.000 minutos = ~3.800 anos, cabe folgado em integer.
const TEMPO_SET_DEFINITIVO = 2000000000;

// Limite de retrocompatibilidade: qualquer valor >= a este já era "definitivo"
// no sistema antigo (inclusive linhas legadas com 999bi salvas em colunas bigint).
const TEMPO_SET_DEFINITIVO_LIMIAR = 1000000000;

/**
 * Migra o prompt antigo (users.prompt_support_agent) para um modelo
 * "Padrão" em prompt_models, deixando-o ativo.
 * NUNCA lança exceção — sempre devolve status pra quem chamou decidir.
 */
function runLegacyMigration(PDO $pdo, string $email): array {
  $out = [
    'status'     => 'failed',
    'step'       => 'start',
    'error'      => '',
    'model_id'   => null,
    'model_name' => null,
  ];

  try {
    // 1) já tem modelos? não migra
    $out['step'] = 'count_existing';
    $cnt = $pdo->prepare('SELECT COUNT(*) FROM prompt_models WHERE user_email = :e');
    $cnt->execute([':e'=>$email]);
    if (((int)$cnt->fetchColumn()) > 0) {
      $out['status'] = 'already_has_models';
      return $out;
    }

    // 2) busca o prompt antigo
    $out['step'] = 'fetch_legacy';
    $u = $pdo->prepare('
      SELECT prompt_support_agent, tempo_de_set
        FROM users WHERE email = :e LIMIT 1
    ');
    $u->execute([':e'=>$email]);
    $urow = $u->fetch(PDO::FETCH_ASSOC) ?: [];

    $oldPrompt = (string)($urow['prompt_support_agent'] ?? '');
    $oldPrompt = str_replace(["\r\n", "\r"], "\n", $oldPrompt);
    $oldPrompt = trim($oldPrompt);

    if ($oldPrompt === '') {
      $out['status'] = 'no_legacy_prompt';
      return $out;
    }

    // 3) sanitiza tempo + tamanho
    $out['step'] = 'sanitize';
    $oldTempo = isset($urow['tempo_de_set']) ? (int)$urow['tempo_de_set'] : 12;
    if ($oldTempo < 0) $oldTempo = 0;
    if ($oldTempo > TEMPO_SET_DEFINITIVO) $oldTempo = TEMPO_SET_DEFINITIVO;
    $oldDefinitivo = ($oldTempo >= TEMPO_SET_DEFINITIVO_LIMIAR);

    if (mb_strlen($oldPrompt, 'UTF-8') > 8000) {
      $oldPrompt = mb_substr($oldPrompt, 0, 8000, 'UTF-8');
    }

    // 4) resolve nome único "Padrão"
    $out['step'] = 'find_unique_name';
    $base = 'Padrão'; $try = $base; $i = 1;
    while (true) {
      $chk = $pdo->prepare('
        SELECT 1 FROM prompt_models
         WHERE user_email = :e AND lower(model_name) = lower(:n) LIMIT 1
      ');
      $chk->execute([':e'=>$email, ':n'=>$try]);
      if (!$chk->fetch()) break;
      $i++; $try = $base . " ($i)";
      if ($i > 99) { $try = $base . ' ' . time(); break; }
    }

    // 5) insere (já desativando qualquer ativo, por segurança)
    $out['step'] = 'insert';
    safeBegin($pdo);
    try {
      $pdo->prepare('
        UPDATE prompt_models
           SET is_active = false, updated_at = now()
         WHERE user_email = :e AND is_active = true
      ')->execute([':e'=>$email]);

      $ins = $pdo->prepare('
        INSERT INTO prompt_models
          (user_email, folder_name, model_name, prompt_text, tempo_de_set, definitivo, is_active, created_at, updated_at)
        VALUES
          (:e, :f, :n, :p, :t, :d, true, now(), now())
        RETURNING id
      ');
      $ins->execute([
        ':e'=>$email, ':f'=>'Geral', ':n'=>$try,
        ':p'=>$oldPrompt, ':t'=>$oldTempo, ':d'=>$oldDefinitivo ? 1 : 0,
      ]);
      $newId = (int)$ins->fetchColumn();

      $pdo->commit();
      $out['status']     = 'migrated';
      $out['model_id']   = $newId;
      $out['model_name'] = $try;
      $out['step']       = 'done';
      return $out;
    } catch (Throwable $e) {
      if ($pdo->inTransaction()) {
        try { $pdo->rollBack(); } catch (Throwable $ignore) {}
      }
      throw $e;
    }
  } catch (Throwable $e) {
    $out['error'] = $e->getMessage();
    return $out;
  }
}

/**
 * Normaliza o "nome" da mídia: minúsculo, sem acento, sem espaços, apenas [a-z0-9_-]
 */
function normalize_media_id(string $s): string {
  $s = mb_strtolower($s, 'UTF-8');
  $trans = [
    'á'=>'a','à'=>'a','ã'=>'a','â'=>'a','ä'=>'a',
    'é'=>'e','è'=>'e','ê'=>'e','ë'=>'e',
    'í'=>'i','ì'=>'i','î'=>'i','ï'=>'i',
    'ó'=>'o','ò'=>'o','õ'=>'o','ô'=>'o','ö'=>'o',
    'ú'=>'u','ù'=>'u','û'=>'u','ü'=>'u',
    'ç'=>'c','ñ'=>'n'
  ];
  $s = strtr($s, $trans);
  $s = preg_replace('/\s+/', '_', $s);
  $s = preg_replace('/[^a-z0-9_-]+/', '_', $s);
  $s = preg_replace('/_+/', '_', $s);
  $s = trim($s, '_-');
  return $s ?: 'midia';
}

function load_media_lib(PDO $pdo, string $email): array {
  $st = $pdo->prepare('SELECT media_biblioteca FROM users WHERE email = :e LIMIT 1');
  $st->execute([':e'=>$email]);
  $val = $st->fetchColumn();
  if (!$val) return [];
  if (is_string($val)) {
    $j = json_decode($val, true);
    return is_array($j) ? $j : [];
  }
  return is_array($val) ? $val : [];
}

function save_media_lib(PDO $pdo, string $email, array $lib): void {
  $st = $pdo->prepare('
    UPDATE users
       SET media_biblioteca = :lib::jsonb,
           updated_at = now()
     WHERE email = :e
  ');
  $st->execute([
    ':lib' => json_encode(array_values($lib), JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
    ':e'   => $email
  ]);
}

function guess_media_type(string $url): string {
  $u = strtolower(parse_url($url, PHP_URL_PATH) ?? $url);
  if (preg_match('/\.(png|jpe?g|webp|gif|bmp|svg)(\?|$)/', $u)) return 'image';
  if (preg_match('/\.(mp4|mov|webm|m4v|avi|mkv)(\?|$)/', $u)) return 'video';
  if (preg_match('/\.(mp3|wav|ogg|m4a|aac|flac)(\?|$)/', $u)) return 'audio';
  if (preg_match('/\.(pdf)(\?|$)/', $u)) return 'pdf';
  return 'file';
}

try {
  switch ($action) {
    // ====== GET prompt/tempo e biblioteca ======
    case 'get': {
      $st = $pdo->prepare('
        SELECT prompt_support_agent, tempo_de_set, media_biblioteca, agenda_link,
               numero_falar_humano, grupo_falar_humano
          FROM users
         WHERE email = :e
         LIMIT 1
      ');
      $st->execute([':e'=>$EMAIL]);
      $row = $st->fetch(PDO::FETCH_ASSOC) ?: [];
      $numero = $row['numero_falar_humano'] ?? '';
      $grupo  = $row['grupo_falar_humano']  ?? '';
      $prompt = $row['prompt_support_agent'] ?? '';
      $tempo  = isset($row['tempo_de_set']) ? (int)$row['tempo_de_set'] : 12;
      $isDefinitivo = ($tempo >= TEMPO_SET_DEFINITIVO_LIMIAR);
      $lib = $row['media_biblioteca'] ?? '[]';
      $libArr = is_string($lib) ? (json_decode($lib, true) ?: []) : ($lib ?: []);
      $agenda = $row['agenda_link'] ?? '';

      jexit([
        'ok' => true,
        'prompt' => $prompt,
        'tempo_de_set' => $tempo,
        'definitivo' => $isDefinitivo,
        'media_biblioteca' => $libArr,
        'agenda_link' => $agenda,
        'numero_falar_humano' => $numero,
        'grupo_falar_humano'  => $grupo
      ]);
    }

    // ====== SAVE prompt/tempo (legado — mantido por compat) ======
    case 'save': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $prompt = (string)($_POST['prompt'] ?? '');
      $prompt = str_replace(["\r\n", "\r"], "\n", $prompt);
      $prompt = trim($prompt);
      $len = mb_strlen($prompt, 'UTF-8');

      if ($len > 8000) {
        jexit([
          'ok' => false,
          'error' => "Limite de 8000 caracteres excedido ($len/8000).",
          'len' => $len
        ], 422);
      }

      $definitivo = ($_POST['definitivo'] ?? '') ? true : false;
      if ($definitivo) {
        $tempoDeSet = TEMPO_SET_DEFINITIVO;
      } else {
        $raw = preg_replace('/\D+/', '', (string)($_POST['tempo_de_set'] ?? ''));
        $tempoDeSet = ($raw === '' ? 12 : (int)$raw);
        if ($tempoDeSet < 0) $tempoDeSet = 0;
        if ($tempoDeSet > TEMPO_SET_DEFINITIVO) $tempoDeSet = TEMPO_SET_DEFINITIVO;
      }

      $st = $pdo->prepare('
        UPDATE users
           SET prompt_support_agent = :p,
               tempo_de_set = :t,
               updated_at = now()
         WHERE email = :e
        RETURNING updated_at
      ');
      $st->execute([
        ':p' => $prompt,
        ':t' => $tempoDeSet,
        ':e' => $EMAIL
      ]);

      jexit([
        'ok' => true,
        'saved' => true,
        'tempo_de_set' => $tempoDeSet,
        'definitivo' => ($tempoDeSet >= TEMPO_SET_DEFINITIVO_LIMIAR)
      ]);
    }

    // ====== MEDIA: listar ======
    case 'media_list': {
      $lib = load_media_lib($pdo, $EMAIL);
      jexit(['ok'=>true, 'data'=>$lib]);
    }

    // ====== MEDIA: adicionar ======
    case 'media_add': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $nameRaw = trim((string)($_POST['name'] ?? ''));
      $url     = trim((string)($_POST['url']  ?? ''));
      $typeIn  = trim((string)($_POST['type'] ?? ''));

      if ($nameRaw === '' || $url === '') {
        jexit(['ok'=>false,'error'=>'Informe nome e URL.'], 422);
      }

      $id = normalize_media_id($nameRaw);
      if ($id === '') $id = 'midia';

      $lib = load_media_lib($pdo, $EMAIL);
      foreach ($lib as $it) {
        if (isset($it['id']) && $it['id'] === $id) {
          jexit(['ok'=>false,'error'=>'Já existe uma mídia com esse nome (ID).'], 422);
        }
      }

      $type = $typeIn !== '' ? $typeIn : guess_media_type($url);
      if (!in_array($type, ['image','video','audio','pdf','file'], true)) {
        $type = 'file';
      }

      $lib[] = [
        'id'         => $id,
        'url'        => $url,
        'type'       => $type,
        'created_at' => date('c'),
      ];

      save_media_lib($pdo, $EMAIL, $lib);
      jexit(['ok'=>true, 'saved'=>true, 'item'=>end($lib)]);
    }

    // ====== MEDIA: excluir ======
    case 'media_delete': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $id = trim((string)($_POST['id'] ?? ''));
      if ($id === '') {
        jexit(['ok'=>false,'error'=>'ID inválido'], 422);
      }

      $lib = load_media_lib($pdo, $EMAIL);
      $new = [];
      $removed = null;

      foreach ($lib as $it) {
        if (($it['id'] ?? '') === $id) {
          $removed = $it;
          continue;
        }
        $new[] = $it;
      }

      if (!$removed) {
        jexit(['ok'=>false,'error'=>'Mídia não encontrada'], 404);
      }

      save_media_lib($pdo, $EMAIL, $new);
      jexit(['ok'=>true, 'deleted'=>true, 'id'=>$id]);
    }

    // ====== AGENDA: salvar link ======
    case 'agenda_save': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $link = trim((string)($_POST['link'] ?? ''));
      if ($link !== '' && !preg_match('~^https?://~i', $link)) {
        jexit(['ok'=>false,'error'=>'Informe uma URL válida (https://...)'], 422);
      }

      $st = $pdo->prepare('
        UPDATE users
           SET agenda_link = :l,
               updated_at = now()
         WHERE email = :e
        RETURNING agenda_link
      ');
      $st->execute([':l'=>$link, ':e'=>$EMAIL]);
      $saved = $st->fetchColumn();

      jexit(['ok'=>true, 'saved'=>true, 'agenda_link'=>$saved]);
    }

    // ====== FALAR HUMANO: salvar número/grupo ======
    case 'falarhumano_save': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $numero = trim((string)($_POST['numero'] ?? ''));
      $grupo  = trim((string)($_POST['grupo']  ?? ''));

      if ($numero !== '' && !preg_match('/^55\d{10,13}$/', $numero)) {
        jexit(['ok'=>false,'error'=>'Informe o número no formato 55DDD99999999'], 422);
      }

      $st = $pdo->prepare('
        UPDATE users
           SET numero_falar_humano = :n,
               grupo_falar_humano  = :g,
               updated_at = now()
         WHERE email = :e
        RETURNING numero_falar_humano, grupo_falar_humano
      ');
      $st->execute([':n'=>$numero, ':g'=>$grupo, ':e'=>$EMAIL]);
      $saved = $st->fetch(PDO::FETCH_ASSOC) ?: [];

      jexit([
        'ok'=>true,
        'saved'=>true,
        'numero_falar_humano'=>$saved['numero_falar_humano'] ?? '',
        'grupo_falar_humano' =>$saved['grupo_falar_humano']  ?? ''
      ]);
    }

    /* ============================================================
       MODELOS DE PROMPT  (tabela public.prompt_models)
       ============================================================ */

    // ------ MODELOS: listar ------
    case 'models_list': {
      // === MIGRAÇÃO AUTOMÁTICA (contas anteriores ao CRUD) ===
      // Tenta UMA VEZ migrar o prompt antigo (users.prompt_support_agent)
      // para um modelo "Padrão". Se falhar, NÃO derruba a listagem —
      // sinaliza needs_migration: true pro front, que mostra um botão
      // "Restaurar meu prompt anterior" pro usuário tentar manualmente.
      $migration = runLegacyMigration($pdo, $EMAIL);

      if ($pdo->inTransaction()) {
        try { $pdo->rollBack(); } catch (Throwable $ignore) {}
      }

      $st = $pdo->prepare('
        SELECT id, folder_name, model_name, prompt_text,
               tempo_de_set, definitivo, is_active,
               created_at, updated_at
          FROM prompt_models
         WHERE user_email = :e
         ORDER BY is_active DESC, lower(model_name) ASC
      ');
      $st->execute([':e'=>$EMAIL]);
      $rows = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];

      $data = array_map(function($r){
        return [
          'id'           => (int)$r['id'],
          'folder_name'  => $r['folder_name'] ?? 'Geral',
          'model_name'   => $r['model_name'],
          'tempo_de_set' => (int)$r['tempo_de_set'],
          'definitivo'   => (bool)$r['definitivo'],
          'is_active'    => (bool)$r['is_active'],
          'updated_at'   => $r['updated_at'],
        ];
      }, $rows);

      $needsMigration = false;
      $legacyPreview  = '';
      if (empty($data)) {
        $u2 = $pdo->prepare('SELECT prompt_support_agent FROM users WHERE email = :e LIMIT 1');
        $u2->execute([':e'=>$EMAIL]);
        $legacy = trim((string)($u2->fetchColumn() ?: ''));
        if ($legacy !== '') {
          $needsMigration = true;
          $legacyPreview = mb_substr($legacy, 0, 200, 'UTF-8');
        }
      }

      jexit([
        'ok' => true,
        'data' => $data,
        'needs_migration' => $needsMigration,
        'legacy_preview'  => $legacyPreview,
        'migration_status' => $migration['status'],
        'migration_step'   => $migration['step'],
        'migration_error'  => $migration['error'],
      ]);
    }

    // ------ MODELOS: migração manual (botão "Restaurar meu prompt") ------
    case 'models_migrate_now': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $m = runLegacyMigration($pdo, $EMAIL);

      if ($m['status'] === 'migrated') {
        jexit([
          'ok' => true,
          'migrated' => true,
          'model_id'   => $m['model_id'],
          'model_name' => $m['model_name'],
        ]);
      }

      if ($m['status'] === 'no_legacy_prompt') {
        jexit(['ok'=>false,'error'=>'Não encontramos prompt antigo para restaurar.'], 404);
      }

      if ($m['status'] === 'already_has_models') {
        jexit(['ok'=>false,'error'=>'Você já tem prompts criados — recarregue a página.'], 409);
      }

      jexit([
        'ok'=>false,
        'error' => 'Falha ao restaurar: ['. $m['step'] .'] '. ($m['error'] ?: 'erro desconhecido'),
        'step'  => $m['step'],
      ], 500);
    }

    // ------ MODELOS: salvar (criar novo) ------
    case 'models_save': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $name = trim((string)($_POST['model_name'] ?? ''));
      if ($name === '') {
        jexit(['ok'=>false,'error'=>'Informe um nome para o modelo.'], 422);
      }
      if (mb_strlen($name, 'UTF-8') > 150) {
        jexit(['ok'=>false,'error'=>'O nome do modelo é muito longo (máx. 150).'], 422);
      }

      $prompt = (string)($_POST['prompt'] ?? '');
      $prompt = str_replace(["\r\n", "\r"], "\n", $prompt);
      $prompt = trim($prompt);
      $len = mb_strlen($prompt, 'UTF-8');
      if ($len > 8000) {
        jexit(['ok'=>false,'error'=>"Limite de 8000 caracteres excedido ($len/8000).",'len'=>$len], 422);
      }

      $definitivo = ($_POST['definitivo'] ?? '') ? true : false;
      if ($definitivo) {
        $tempoDeSet = TEMPO_SET_DEFINITIVO;
      } else {
        $raw = preg_replace('/\D+/', '', (string)($_POST['tempo_de_set'] ?? ''));
        $tempoDeSet = ($raw === '' ? 12 : (int)$raw);
        if ($tempoDeSet < 0) $tempoDeSet = 0;
        if ($tempoDeSet > TEMPO_SET_DEFINITIVO) $tempoDeSet = TEMPO_SET_DEFINITIVO;
      }

      $dup = $pdo->prepare('
        SELECT 1 FROM prompt_models
         WHERE user_email = :e AND lower(model_name) = lower(:n)
         LIMIT 1
      ');
      $dup->execute([':e'=>$EMAIL, ':n'=>$name]);
      if ($dup->fetch()) {
        jexit(['ok'=>false,'error'=>'Você já tem um modelo com esse nome.'], 409);
      }

      $ins = $pdo->prepare('
        INSERT INTO prompt_models
          (user_email, folder_name, model_name, prompt_text, tempo_de_set, definitivo, is_active, created_at, updated_at)
        VALUES
          (:e, :f, :n, :p, :t, :d, false, now(), now())
        RETURNING id, model_name, folder_name, tempo_de_set, definitivo, is_active, updated_at
      ');
      $ins->execute([
        ':e'=>$EMAIL,
        ':f'=>'Geral',
        ':n'=>$name,
        ':p'=>$prompt,
        ':t'=>$tempoDeSet,
        ':d'=>$definitivo ? 1 : 0,
      ]);
      $row = $ins->fetch(PDO::FETCH_ASSOC) ?: [];

      jexit([
        'ok'=>true,
        'saved'=>true,
        'model'=>[
          'id'           => (int)($row['id'] ?? 0),
          'model_name'   => $row['model_name'] ?? $name,
          'folder_name'  => $row['folder_name'] ?? 'Geral',
          'tempo_de_set' => (int)($row['tempo_de_set'] ?? $tempoDeSet),
          'definitivo'   => (bool)($row['definitivo'] ?? $definitivo),
          'is_active'    => (bool)($row['is_active'] ?? false),
          'updated_at'   => $row['updated_at'] ?? null,
        ]
      ]);
    }

    // ------ MODELOS: usar (marca ativo + devolve conteúdo) ------
    case 'models_use': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $id = (int)($_POST['id'] ?? 0);
      if ($id <= 0) {
        jexit(['ok'=>false,'error'=>'ID inválido.'], 422);
      }

      $own = $pdo->prepare('
        SELECT id, model_name, prompt_text, tempo_de_set, definitivo
          FROM prompt_models
         WHERE id = :id AND user_email = :e
         LIMIT 1
      ');
      $own->execute([':id'=>$id, ':e'=>$EMAIL]);
      $model = $own->fetch(PDO::FETCH_ASSOC);
      if (!$model) {
        jexit(['ok'=>false,'error'=>'Modelo não encontrado.'], 404);
      }

      $modelTempo = (int)$model['tempo_de_set'];

      safeBegin($pdo);
      try {
        // 1) aplica no agente (users)
        $pdo->prepare('
          UPDATE users
             SET prompt_support_agent = :p,
                 tempo_de_set = :t,
                 updated_at = now()
           WHERE email = :e
        ')->execute([
          ':p' => (string)$model['prompt_text'],
          ':t' => $modelTempo,
          ':e' => $EMAIL
        ]);

        // 2) zera todos os ativos antes de ativar este
        // (índice único parcial exige essa ordem)
        $pdo->prepare('
          UPDATE prompt_models
             SET is_active = false, updated_at = now()
           WHERE user_email = :e AND is_active = true
        ')->execute([':e'=>$EMAIL]);

        $pdo->prepare('
          UPDATE prompt_models
             SET is_active = true, updated_at = now()
           WHERE id = :id AND user_email = :e
        ')->execute([':id'=>$id, ':e'=>$EMAIL]);

        $pdo->commit();
      } catch (Throwable $e) {
        if ($pdo->inTransaction()) {
          try { $pdo->rollBack(); } catch (Throwable $ignore) {}
        }
        throw $e;
      }

      $tempo = $modelTempo;

      jexit([
        'ok'=>true,
        'used'=>true,
        'model'=>[
          'id'           => (int)$model['id'],
          'model_name'   => $model['model_name'],
          'prompt_text'  => (string)$model['prompt_text'],
          'tempo_de_set' => $tempo,
          'definitivo'   => (bool)$model['definitivo'] || ($tempo >= TEMPO_SET_DEFINITIVO_LIMIAR),
        ]
      ]);
    }

    // ------ MODELOS: excluir ------
    case 'models_delete': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $id = (int)($_POST['id'] ?? 0);
      if ($id <= 0) {
        jexit(['ok'=>false,'error'=>'ID inválido.'], 422);
      }

      $own = $pdo->prepare('SELECT 1 FROM prompt_models WHERE id = :id AND user_email = :e');
      $own->execute([':id'=>$id, ':e'=>$EMAIL]);
      if (!$own->fetch()) {
        jexit(['ok'=>false,'error'=>'Modelo não encontrado.'], 404);
      }

      $pdo->prepare('DELETE FROM prompt_models WHERE id = :id AND user_email = :e')
          ->execute([':id'=>$id, ':e'=>$EMAIL]);

      jexit(['ok'=>true, 'deleted'=>true, 'id'=>$id]);
    }

    // ------ MODELOS: salvar edição ------
    // Atualiza prompt/tempo/definitivo de um modelo existente.
    // NÃO altera qual modelo está ativo. Se o modelo editado for
    // o que está Em uso, espelha a alteração no agente (users)
    // pra ele rodar a versão recém-salva.
    case 'models_update': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $id = (int)($_POST['id'] ?? 0);
      if ($id <= 0) {
        jexit(['ok'=>false,'error'=>'ID inválido.'], 422);
      }

      $own = $pdo->prepare('
        SELECT id, is_active FROM prompt_models
         WHERE id = :id AND user_email = :e LIMIT 1
      ');
      $own->execute([':id'=>$id, ':e'=>$EMAIL]);
      $cur = $own->fetch(PDO::FETCH_ASSOC);
      if (!$cur) {
        jexit(['ok'=>false,'error'=>'Modelo não encontrado.'], 404);
      }

      $prompt = (string)($_POST['prompt'] ?? '');
      $prompt = str_replace(["\r\n", "\r"], "\n", $prompt);
      $prompt = trim($prompt);
      $len = mb_strlen($prompt, 'UTF-8');
      if ($len > 8000) {
        jexit(['ok'=>false,'error'=>"Limite de 8000 caracteres excedido ($len/8000).",'len'=>$len], 422);
      }

      $definitivo = ($_POST['definitivo'] ?? '') ? true : false;
      if ($definitivo) {
        $tempoDeSet = TEMPO_SET_DEFINITIVO;
      } else {
        $raw = preg_replace('/\D+/', '', (string)($_POST['tempo_de_set'] ?? ''));
        $tempoDeSet = ($raw === '' ? 12 : (int)$raw);
        if ($tempoDeSet < 0) $tempoDeSet = 0;
        if ($tempoDeSet > TEMPO_SET_DEFINITIVO) $tempoDeSet = TEMPO_SET_DEFINITIVO;
      }

      $isActive = (bool)$cur['is_active'];

      safeBegin($pdo);
      try {
        $pdo->prepare('
          UPDATE prompt_models
             SET prompt_text = :p,
                 tempo_de_set = :t,
                 definitivo = :d,
                 updated_at = now()
           WHERE id = :id AND user_email = :e
        ')->execute([
          ':p'=>$prompt, ':t'=>$tempoDeSet, ':d'=>$definitivo ? 1 : 0,
          ':id'=>$id, ':e'=>$EMAIL
        ]);

        // se é o modelo ativo, espelha no agente
        if ($isActive) {
          $pdo->prepare('
            UPDATE users
               SET prompt_support_agent = :p,
                   tempo_de_set = :t,
                   updated_at = now()
             WHERE email = :e
          ')->execute([':p'=>$prompt, ':t'=>$tempoDeSet, ':e'=>$EMAIL]);
        }

        $pdo->commit();
      } catch (Throwable $e) {
        if ($pdo->inTransaction()) {
          try { $pdo->rollBack(); } catch (Throwable $ignore) {}
        }
        throw $e;
      }

      jexit([
        'ok'=>true,
        'updated'=>true,
        'id'=>$id,
        'is_active'=>$isActive,
        'tempo_de_set'=>$tempoDeSet,
        'definitivo'=>($tempoDeSet >= TEMPO_SET_DEFINITIVO_LIMIAR),
      ]);
    }

    // ------ MODELOS: renomear ------
    case 'models_rename': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $id = (int)($_POST['id'] ?? 0);
      if ($id <= 0) {
        jexit(['ok'=>false,'error'=>'ID inválido.'], 422);
      }

      $name = trim((string)($_POST['model_name'] ?? ''));
      if ($name === '') {
        jexit(['ok'=>false,'error'=>'Informe um nome para o modelo.'], 422);
      }
      if (mb_strlen($name, 'UTF-8') > 150) {
        jexit(['ok'=>false,'error'=>'O nome do modelo é muito longo (máx. 150).'], 422);
      }

      $own = $pdo->prepare('SELECT 1 FROM prompt_models WHERE id = :id AND user_email = :e');
      $own->execute([':id'=>$id, ':e'=>$EMAIL]);
      if (!$own->fetch()) {
        jexit(['ok'=>false,'error'=>'Modelo não encontrado.'], 404);
      }

      $dup = $pdo->prepare('
        SELECT 1 FROM prompt_models
         WHERE user_email = :e AND lower(model_name) = lower(:n) AND id <> :id
         LIMIT 1
      ');
      $dup->execute([':e'=>$EMAIL, ':n'=>$name, ':id'=>$id]);
      if ($dup->fetch()) {
        jexit(['ok'=>false,'error'=>'Você já tem outro modelo com esse nome.'], 409);
      }

      $up = $pdo->prepare('
        UPDATE prompt_models
           SET model_name = :n, updated_at = now()
         WHERE id = :id AND user_email = :e
        RETURNING id, model_name
      ');
      $up->execute([':n'=>$name, ':id'=>$id, ':e'=>$EMAIL]);
      $row = $up->fetch(PDO::FETCH_ASSOC) ?: [];

      jexit([
        'ok'=>true,
        'renamed'=>true,
        'id'=>(int)($row['id'] ?? $id),
        'model_name'=>$row['model_name'] ?? $name
      ]);
    }

    // ------ MODELOS: importar (1 modelo via JSON) ------
    case 'models_import': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      $raw = (string)($_POST['payload'] ?? '');
      if ($raw === '') {
        jexit(['ok'=>false,'error'=>'Arquivo vazio ou inválido.'], 422);
      }

      $j = json_decode($raw, true);
      if (!is_array($j)) {
        jexit(['ok'=>false,'error'=>'JSON inválido. Verifique o arquivo.'], 422);
      }

      $m = isset($j['model']) && is_array($j['model']) ? $j['model'] : $j;

      $name = trim((string)($m['model_name'] ?? ''));
      if ($name === '') {
        jexit(['ok'=>false,'error'=>'O arquivo não contém um nome de modelo válido.'], 422);
      }
      if (mb_strlen($name, 'UTF-8') > 150) {
        $name = mb_substr($name, 0, 150, 'UTF-8');
      }

      $prompt = (string)($m['prompt_text'] ?? $m['prompt'] ?? '');
      $prompt = str_replace(["\r\n", "\r"], "\n", $prompt);
      $prompt = trim($prompt);
      $len = mb_strlen($prompt, 'UTF-8');
      if ($len > 8000) {
        jexit(['ok'=>false,'error'=>"O prompt do arquivo excede 8000 caracteres ($len/8000)."], 422);
      }

      $definitivo = !empty($m['definitivo']);
      if ($definitivo) {
        $tempoDeSet = TEMPO_SET_DEFINITIVO;
      } else {
        $raw2 = preg_replace('/\D+/', '', (string)($m['tempo_de_set'] ?? '12'));
        $tempoDeSet = ($raw2 === '' ? 12 : (int)$raw2);
        if ($tempoDeSet < 0) $tempoDeSet = 0;
        if ($tempoDeSet > TEMPO_SET_DEFINITIVO) $tempoDeSet = TEMPO_SET_DEFINITIVO;
      }

      $base = $name;
      $try  = $name;
      $i    = 1;
      while (true) {
        $chk = $pdo->prepare('
          SELECT 1 FROM prompt_models
           WHERE user_email = :e AND lower(model_name) = lower(:n) LIMIT 1
        ');
        $chk->execute([':e'=>$EMAIL, ':n'=>$try]);
        if (!$chk->fetch()) break;
        $i++;
        $suffix = " ($i)";
        $maxBase = 150 - mb_strlen($suffix, 'UTF-8');
        $try = mb_substr($base, 0, $maxBase, 'UTF-8') . $suffix;
        if ($i > 99) {
          jexit(['ok'=>false,'error'=>'Não foi possível gerar um nome único para o modelo.'], 409);
        }
      }

      $ins = $pdo->prepare('
        INSERT INTO prompt_models
          (user_email, folder_name, model_name, prompt_text, tempo_de_set, definitivo, is_active, created_at, updated_at)
        VALUES
          (:e, :f, :n, :p, :t, :d, false, now(), now())
        RETURNING id, model_name, folder_name, tempo_de_set, definitivo, is_active, updated_at
      ');
      $ins->execute([
        ':e'=>$EMAIL,
        ':f'=>'Geral',
        ':n'=>$try,
        ':p'=>$prompt,
        ':t'=>$tempoDeSet,
        ':d'=>$definitivo ? 1 : 0,
      ]);
      $row = $ins->fetch(PDO::FETCH_ASSOC) ?: [];

      jexit([
        'ok'=>true,
        'imported'=>true,
        'renamed'=>($try !== $base),
        'model'=>[
          'id'           => (int)($row['id'] ?? 0),
          'model_name'   => $row['model_name'] ?? $try,
          'folder_name'  => $row['folder_name'] ?? 'Geral',
          'tempo_de_set' => (int)($row['tempo_de_set'] ?? $tempoDeSet),
          'definitivo'   => (bool)($row['definitivo'] ?? $definitivo),
          'is_active'    => (bool)($row['is_active'] ?? false),
          'updated_at'   => $row['updated_at'] ?? null,
        ]
      ]);
    }

    // ------ MODELOS: baixar 1 (JSON completo c/ prompt_text) ------
    case 'models_export': {
      $id = (int)($_GET['id'] ?? 0);
      if ($id <= 0) {
        jexit(['ok'=>false,'error'=>'ID inválido.'], 422);
      }

      $st = $pdo->prepare('
        SELECT model_name, prompt_text, tempo_de_set, definitivo
          FROM prompt_models
         WHERE id = :id AND user_email = :e
         LIMIT 1
      ');
      $st->execute([':id'=>$id, ':e'=>$EMAIL]);
      $m = $st->fetch(PDO::FETCH_ASSOC);
      if (!$m) {
        jexit(['ok'=>false,'error'=>'Modelo não encontrado.'], 404);
      }

      jexit([
        'ok'=>true,
        'model'=>[
          'model_name'   => $m['model_name'],
          'prompt_text'  => (string)$m['prompt_text'],
          'tempo_de_set' => (int)$m['tempo_de_set'],
          'definitivo'   => (bool)$m['definitivo'],
        ],
        '_evolua_export' => 'prompt_model_v1'
      ]);
    }

    // ====== LIMPAR MEMÓRIA (TESTES) ======
    case 'clear_memory': {
      $token = $_POST['csrf'] ?? $_SERVER['HTTP_X_CSRF'] ?? '';
      if (!csrf_verify($token)) {
        jexit(['ok'=>false,'error'=>'Sessão expirada. Recarregue a página.'], 403);
      }

      if (!defined('AGENT_CLEAR_MEMORY_WEBHOOK') || !AGENT_CLEAR_MEMORY_WEBHOOK) {
        jexit(['ok'=>false,'error'=>'Webhook não configurado no config.php'], 500);
      }

      $numero = trim((string)($_POST['numero'] ?? ''));
      if ($numero === '' || !preg_match('/^55\d{10,13}$/', $numero)) {
        jexit(['ok'=>false,'error'=>'Informe o número no formato 55DDD99999999'], 422);
      }

      $payload = [
        'event' => 'agent_clear_memory',
        'email' => $EMAIL,
        'numero_teste' => $numero,
        'ts' => time(),
      ];

      $ch = curl_init(AGENT_CLEAR_MEMORY_WEBHOOK);
      curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json; charset=utf-8'],
        CURLOPT_POSTFIELDS => json_encode($payload, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES),
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 20,
      ]);

      $resp = curl_exec($ch);
      $err  = curl_error($ch);
      $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);

      if ($resp === false) {
        jexit(['ok'=>false,'error'=>'Falha ao chamar webhook: '.$err], 502);
      }
      if ($code < 200 || $code >= 300) {
        jexit(['ok'=>false,'error'=>"Webhook retornou HTTP $code"], 502);
      }

      jexit([
        'ok' => true,
        'cleared' => true,
        'numero_teste' => $numero
      ]);
    }

    default:
      jexit(['ok'=>false,'error'=>'Ação inválida'], 400);
  }
} catch (Throwable $e) {
  // CRÍTICO: se sobrou transação aberta/abortada, limpa AQUI.
  // Sem isso, a conexão (reaproveitada pelo pooler do Supabase) fica
  // "suja" e o PRÓXIMO request estoura SQLSTATE[25P02].
  try {
    if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
      $pdo->rollBack();
    }
  } catch (Throwable $ignore) {
    try { $pdo->exec('ROLLBACK'); } catch (Throwable $ignore2) {}
  }

  $msg = $e->getMessage();
  if (strpos($msg, '25P02') !== false) {
    $msg = 'A conexão estava ocupada. Tente novamente — já liberei o estado.';
  }
  jexit(['ok'=>false,'error'=>$msg], 500);
}