<?php
// public/api/crm.php - VERSÃO CORRIGIDA DEFINITIVA

$ROOT = dirname(__DIR__, 2);
require_once $ROOT . '/app/config.php';
require_once $ROOT . '/app/db.php';
require_once $ROOT . '/app/auth_middleware.php';

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

$action = $_GET['action'] ?? '';

// ===== AUTENTICAÇÃO =====
$publicRoutes = ['followup_webhook', 'followup_sent', 'followup_move_after_send'];

if (in_array($action, $publicRoutes)) {
  $token = $_GET['token'] ?? $_POST['token'] ?? '';
  $expectedToken = defined('N8N_WEBHOOK_TOKEN') ? N8N_WEBHOOK_TOKEN : '';
  
  if (!$expectedToken || $token !== $expectedToken) {
    http_response_code(403);
    echo json_encode(['ok' => false, 'error' => 'Token inválido'], JSON_UNESCAPED_UNICODE);
    exit;
  }
  
  $userIdStr = '';
} else {
  if (function_exists('require_auth_or_401')) { 
    require_auth_or_401(); 
  } else { 
    require_auth_or_redirect(); 
  }
  
  $userIdStr = (string)($_SESSION['user']['id'] ?? '');
}

$pdo = db();

// -------------------- Helpers --------------------
function jfail($msg, $code = 400) {
  http_response_code($code);
  echo json_encode(['ok' => false, 'error' => $msg], JSON_UNESCAPED_UNICODE);
  exit;
}

function jok($data = []) {
  echo json_encode(['ok' => true] + $data, JSON_UNESCAPED_UNICODE);
  exit;
}

function hasColumn(PDO $pdo, string $schema, string $table, string $col): bool {
  $sql = "SELECT 1 FROM information_schema.columns
          WHERE table_schema=:s AND table_name=:t AND column_name=:c";
  $st = $pdo->prepare($sql);
  $st->execute([':s'=>$schema, ':t'=>$table, ':c'=>$col]);
  $result = (bool)$st->fetchColumn();
  $st->closeCursor();
  return $result;
}

function pickFirstExisting(PDO $pdo, string $schema, string $table, array $candidates): ?string {
  foreach ($candidates as $c) if (hasColumn($pdo, $schema, $table, $c)) return $c;
  return null;
}

function colType(PDO $pdo, string $schema, string $table, string $col): ?string {
  $sql = "SELECT data_type, udt_name FROM information_schema.columns
          WHERE table_schema=:s AND table_name=:t AND column_name=:c";
  $st = $pdo->prepare($sql);
  $st->execute([':s'=>$schema, ':t'=>$table, ':c'=>$col]);
  $r = $st->fetch(PDO::FETCH_ASSOC);
  $st->closeCursor();
  
  if ($r) {
    $dt = strtolower((string)$r['data_type']);
    $ud = strtolower((string)$r['udt_name']);
    if ($dt === 'uuid' || $ud === 'uuid') return 'uuid';
    if (str_contains($dt, 'integer') || $ud === 'int4' || $ud === 'int8') return 'integer';
    if ($dt === 'text' || str_contains($dt, 'char')) return 'text';
    return $dt ?: $ud;
  }
  return null;
}

function getStages(PDO $pdo, string $userIdStr) {
  $st = $pdo->prepare("
    SELECT id, name, position
    FROM public.crm_stages
    WHERE user_id = :u
    ORDER BY position, id
  ");
  $st->execute([':u' => $userIdStr]);
  $result = $st->fetchAll(PDO::FETCH_ASSOC);
  $st->closeCursor();
  return $result;
}

function ensureDefaultBoard(PDO $pdo, string $userIdStr) {
  $st = $pdo->prepare("SELECT COUNT(*)::int FROM public.crm_stages WHERE user_id=:u");
  $st->execute([':u' => $userIdStr]);
  $count = (int)$st->fetchColumn();
  $st->closeCursor();
  
  if ($count > 0) return;

  $defaults = ['Leads', 'Em contato', 'Proposta', 'Fechado'];
  $pdo->beginTransaction();
  try {
    $ins = $pdo->prepare("INSERT INTO public.crm_stages (user_id, name, position) VALUES (:u, :n, :p)");
    foreach ($defaults as $p => $name) {
      $ins->execute([':u' => $userIdStr, ':n' => $name, ':p' => $p]);
    }
    $ins->closeCursor();
    $pdo->commit();
  } catch (Exception $e) {
    $pdo->rollBack();
    throw $e;
  }
}

function detectLeadsSchema(PDO $pdo): array {
  $schema = 'public';
  $table  = 'leads';
  $colId = 'id';
  $colCompany = pickFirstExisting($pdo, $schema, $table, ['company_name','company','empresa','nome_empresa','razao_social','nome']);
  $colPhone = pickFirstExisting($pdo, $schema, $table, ['phone','telefone','fone','celular','whatsapp']);
  $colStatus = pickFirstExisting($pdo, $schema, $table, ['status']);
  $colCreatedAt = pickFirstExisting($pdo, $schema, $table, ['created_at','criado_em','createdat','dt_criacao']);
  $colUserId = pickFirstExisting($pdo, $schema, $table, ['user_id','userid','owner_id']);
  $colStageId = pickFirstExisting($pdo, $schema, $table, ['stage_id']);
  $colStagePos = pickFirstExisting($pdo, $schema, $table, ['stage_pos']);
  return compact('schema','table','colId','colCompany','colPhone','colStatus','colCreatedAt','colUserId','colStageId','colStagePos');
}

// -------------------- Rotas --------------------
try {
  

  
  // ====== board_meta ======
  if ($action === 'board_meta') {
    ensureDefaultBoard($pdo, $userIdStr);
    $stages = getStages($pdo, $userIdStr);
    $cfg = detectLeadsSchema($pdo);
    extract($cfg);

    $qL = $pdo->prepare("SELECT id FROM public.crm_stages WHERE user_id = ? AND name = 'Leads' LIMIT 1");
    $qL->execute([$userIdStr]);
    $leadsStageId = (int)($qL->fetch()['id'] ?? 0);
    $qL->closeCursor();

    $counts = [];
    if ($colStageId) {
      $sql = "SELECT CASE WHEN {$colStageId} IS NULL THEN ? ELSE {$colStageId} END AS sid, COUNT(*)::int AS total FROM {$schema}.{$table}";
      $params = [$leadsStageId];
      
      if ($colUserId) {
        $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
        $sql .= ($t === 'uuid') ? " WHERE {$colUserId} = ?::uuid" : " WHERE {$colUserId} = ?";
        $params[] = $userIdStr;
      }
      $sql .= " GROUP BY sid";
      
      $st = $pdo->prepare($sql);
      $st->execute($params);
      foreach ($st->fetchAll(PDO::FETCH_ASSOC) as $r) {
        $counts[(int)$r['sid']] = (int)$r['total'];
      }
      $st->closeCursor();
    } else {
      $sql = "SELECT COUNT(*)::int FROM {$schema}.{$table}";
      $params = [];
      
      if ($colUserId) {
        $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
        $sql .= ($t === 'uuid') ? " WHERE {$colUserId} = ?::uuid" : " WHERE {$colUserId} = ?";
        $params[] = $userIdStr;
      }
      
      $st = $pdo->prepare($sql);
      $st->execute($params);
      $counts[$leadsStageId] = (int)$st->fetchColumn();
      $st->closeCursor();
    }

    $followupStatus = [];
    $stF = $pdo->prepare("SELECT stage_id, active FROM public.crm_followup_configs WHERE user_id = ?");
    $stF->execute([$userIdStr]);
    foreach ($stF->fetchAll(PDO::FETCH_ASSOC) as $f) {
      $followupStatus[(int)$f['stage_id']] = (bool)$f['active'];
    }
    $stF->closeCursor();

    foreach ($stages as &$s) {
      $sid = (int)$s['id'];
      $s['total'] = $counts[$sid] ?? 0;
      $s['has_followup'] = $followupStatus[$sid] ?? false;
    }

    jok(['stages' => $stages]);
  }

  // ====== cards_page ======
  if ($action === 'cards_page') {
    $cfg = detectLeadsSchema($pdo);
    extract($cfg);

    $stage_id = (int)($_GET['stage_id'] ?? 0);
    $page = max(1, (int)($_GET['page'] ?? 1));
    $per = max(1, min(200, (int)($_GET['per'] ?? 100)));
    $q = trim($_GET['q'] ?? '');

    if ($stage_id <= 0) jfail('stage_id inválido', 400);

    $stg = $pdo->prepare("SELECT name FROM public.crm_stages WHERE id=:id AND user_id=:u LIMIT 1");
    $stg->execute([':id'=>$stage_id, ':u'=>$userIdStr]);
    $stageRow = $stg->fetch(PDO::FETCH_ASSOC);
    $stg->closeCursor();
    if (!$stageRow) jfail('Coluna não encontrada', 404);
    $stageName = (string)$stageRow['name'];

    $offset = ($page - 1) * $per;

    $sel = [
      "{$colId} AS id",
      $colCompany ? "$colCompany AS company" : "''::text AS company",
      $colPhone ? "$colPhone AS phone" : "''::text AS phone",
      $colStatus ? "$colStatus AS status" : "NULL::text AS status",
      $colCreatedAt ? "$colCreatedAt AS created_at" : "NOW() AS created_at"
    ];
    $sqlBase = " FROM {$schema}.{$table}";
    $where = [];
    $params = [];

    if ($colStageId) {
      if (strtolower($stageName) === 'leads') {
        $where[] = "({$colStageId} = :sid OR {$colStageId} IS NULL)";
      } else {
        $where[] = "{$colStageId} = :sid";
      }
      $params[':sid'] = $stage_id;
    } else {
      if (strtolower($stageName) !== 'leads') {
        jok(['stage_id' => $stage_id, 'page'=>$page, 'per'=>$per, 'total' => 0, 'has_more' => false, 'cards' => []]);
      }
    }

    if ($colUserId) {
      $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
      if ($t === 'uuid') { $where[] = "{$colUserId} = :u::uuid"; $params[':u'] = $userIdStr; }
      else { $where[] = "{$colUserId} = :u"; $params[':u'] = $userIdStr; }
    }

    if ($q !== '') {
      $digits = preg_replace('/\D+/', '', $q);
      if ($digits !== '' && $colPhone) {
        $where[] = "regexp_replace({$colPhone}, '\\D+', '', 'g') ILIKE :qdigits";
        $params[':qdigits'] = "%{$digits}%";
      } else {
        $chunks = [];
        if ($colCompany) $chunks[] = "{$colCompany} ILIKE :q";
        if ($colPhone) $chunks[] = "{$colPhone} ILIKE :q";
        if (!empty($chunks)) {
          $where[] = '(' . implode(' OR ', $chunks) . ')';
          $params[':q'] = "%{$q}%";
        }
      }
    }

    $whereSql = '';
    if (!empty($where)) $whereSql = ' WHERE ' . implode(' AND ', $where);

    $sqlCount = "SELECT COUNT(*)" . $sqlBase . $whereSql;
    $stmtC = $pdo->prepare($sqlCount);
    foreach ($params as $k=>$v) $stmtC->bindValue($k, $v);
    $stmtC->execute();
    $total = (int)$stmtC->fetchColumn();
    $stmtC->closeCursor();

    $order = $colStagePos ? " ORDER BY {$colStagePos} ASC, {$colId} ASC" : " ORDER BY {$colId} ASC";
    $sql = "SELECT " . implode(", ", $sel) . $sqlBase . $whereSql . $order . " LIMIT :per OFFSET :off";

    $stmt = $pdo->prepare($sql);
    foreach ($params as $k=>$v) $stmt->bindValue($k, $v);
    $stmt->bindValue(':per', $per, PDO::PARAM_INT);
    $stmt->bindValue(':off', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $cards = $stmt->fetchAll(PDO::FETCH_ASSOC);
    $stmt->closeCursor();

    $has_more = ($page * $per) < $total;

    jok(['stage_id' => $stage_id, 'page' => $page, 'per' => $per, 'total' => $total, 'has_more' => $has_more, 'cards' => $cards]);
  }

  // ===== stage_create =====
  if ($action === 'stage_create') {
    $name = trim($_POST['name'] ?? '');
    if ($name === '') jfail('Nome obrigatório');

    $q = $pdo->prepare("SELECT COALESCE(MAX(position), -1)+1 FROM public.crm_stages WHERE user_id=:u");
    $q->execute([':u' => $userIdStr]);
    $pos = (int)$q->fetchColumn();
    $q->closeCursor();

    $ins = $pdo->prepare("INSERT INTO public.crm_stages(user_id, name, position) VALUES(:u, :n, :p) RETURNING id, name, position");
    $ins->execute([':u'=>$userIdStr, ':n'=>$name, ':p'=>$pos]);
    $row = $ins->fetch(PDO::FETCH_ASSOC);
    $ins->closeCursor();
    jok(['stage' => $row]);
  }

  // ===== stage_rename =====
  if ($action === 'stage_rename') {
    $id = (int)($_POST['id'] ?? 0);
    $name = trim($_POST['name'] ?? '');
    if (!$id || $name==='') jfail('Dados inválidos');

    $up = $pdo->prepare("UPDATE public.crm_stages SET name=:n WHERE id=:id AND user_id=:u");
    $up->execute([':n'=>$name, ':id'=>$id, ':u'=>$userIdStr]);
    $up->closeCursor();
    jok();
  }

  // ===== stage_delete =====
  if ($action === 'stage_delete') {
    $id = (int)($_POST['id'] ?? 0);
    if (!$id) jfail('ID inválido');

    $chk = $pdo->prepare("SELECT name FROM public.crm_stages WHERE id=:id AND user_id=:u");
    $chk->execute([':id'=>$id, ':u'=>$userIdStr]);
    $row = $chk->fetch();
    $chk->closeCursor();
    if (!$row) jfail('Não encontrado', 404);
    if ($row['name'] === 'Leads') jfail('A coluna padrão "Leads" não pode ser excluída');

    $qL = $pdo->prepare("SELECT id FROM public.crm_stages WHERE user_id=:u AND name='Leads' LIMIT 1");
    $qL->execute([':u' => $userIdStr]);
    $leadsStageId = (int)($qL->fetch()['id'] ?? 0);
    $qL->closeCursor();

    $cfg = detectLeadsSchema($pdo);
    extract($cfg);

    $pdo->beginTransaction();
    try {
      if ($colStageId) {
        $sql = "UPDATE {$schema}.{$table} SET {$colStageId}=:ls WHERE {$colStageId}=:sid";
        $params = [':ls'=>$leadsStageId, ':sid'=>$id];
        if ($colUserId) {
          $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
          if ($t === 'uuid') { $sql .= " AND {$colUserId}=:u::uuid"; $params[':u']=$userIdStr; }
          else { $sql .= " AND {$colUserId}=:u"; $params[':u']=$userIdStr; }
        }
        $upd = $pdo->prepare($sql);
        $upd->execute($params);
        $upd->closeCursor();
      }

      $del = $pdo->prepare("DELETE FROM public.crm_stages WHERE id=:id AND user_id=:u");
      $del->execute([':id'=>$id, ':u'=>$userIdStr]);
      $del->closeCursor();
      
      $pdo->commit();
      jok();
    } catch (Exception $e) {
      $pdo->rollBack();
      throw $e;
    }
  }

  // ===== stage_reorder =====
  if ($action === 'stage_reorder') {
    $payload = json_decode(file_get_contents('php://input'), true);
    $ids = $payload['ids'] ?? null;
    if (!is_array($ids)) jfail('Formato inválido');

    $pdo->beginTransaction();
    try {
      $pos = 0;
      $up = $pdo->prepare("UPDATE public.crm_stages SET position=:p WHERE id=:id AND user_id=:u");
      foreach ($ids as $sid) {
        $up->execute([':p'=>$pos++, ':id'=>(int)$sid, ':u'=>$userIdStr]);
      }
      $up->closeCursor();
      $pdo->commit();
      jok();
    } catch (Exception $e) {
      $pdo->rollBack();
      throw $e;
    }
  }

  // ===== move_card - VERSÃO TOTALMENTE CORRIGIDA =====
  if ($action === 'move_card') {
    $payload = json_decode(file_get_contents('php://input'), true);
    $leadId = (int)($payload['lead_id'] ?? 0);
    $toStage = (int)($payload['to_stage_id'] ?? 0);
    $toIdx = (int)($payload['to_index'] ?? 0);
    if (!$leadId || !$toStage) jfail('Dados inválidos');

    $cfg = detectLeadsSchema($pdo);
    extract($cfg);

    if (!$colStageId) jfail('A coluna stage_id não existe em leads');
    
    // Buscar stage de origem
    $oldStageId = null;
    $stOld = $pdo->query("SELECT {$colStageId} AS old_stage FROM {$schema}.{$table} WHERE {$colId} = {$leadId} LIMIT 1");
    if ($stOld) {
      $rowOld = $stOld->fetch(PDO::FETCH_ASSOC);
      if ($rowOld) $oldStageId = (int)$rowOld['old_stage'];
      $stOld->closeCursor();
    }

    // Iniciar transação
    $pdo->beginTransaction();

    try {
      // Atualizar posições e mover lead
      if ($colStagePos) {
        $sqlInc = "UPDATE {$schema}.{$table} SET {$colStagePos} = {$colStagePos} + 1 WHERE {$colStageId} = {$toStage} AND {$colStagePos} >= {$toIdx}";
        if ($colUserId) {
          $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
          $sqlInc .= ($t === 'uuid') ? " AND {$colUserId} = '{$userIdStr}'::uuid" : " AND {$colUserId} = '{$userIdStr}'";
        }
        $pdo->exec($sqlInc);

        $sqlMv = "UPDATE {$schema}.{$table} SET {$colStageId}={$toStage}, {$colStagePos}={$toIdx} WHERE {$colId}={$leadId}";
        if ($colUserId) {
          $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
          $sqlMv .= ($t === 'uuid') ? " AND {$colUserId} = '{$userIdStr}'::uuid" : " AND {$colUserId} = '{$userIdStr}'";
        }
        $pdo->exec($sqlMv);
      } else {
        $sqlMv = "UPDATE {$schema}.{$table} SET {$colStageId}={$toStage} WHERE {$colId}={$leadId}";
        if ($colUserId) {
          $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
          $sqlMv .= ($t === 'uuid') ? " AND {$colUserId} = '{$userIdStr}'::uuid" : " AND {$colUserId} = '{$userIdStr}'";
        }
        $pdo->exec($sqlMv);
      }

      // Deletar follow-ups antigos
      if ($oldStageId && $oldStageId !== $toStage) {
        $pdo->exec("
          DELETE FROM public.crm_followup_queue 
          WHERE lead_id = {$leadId} 
          AND config_id IN (
            SELECT id FROM public.crm_followup_configs 
            WHERE user_id = '{$userIdStr}' AND stage_id = {$oldStageId}
          )
          AND status = 'pending'
        ");
      }

      // Verificar follow-up na nova coluna
      $stF = $pdo->query("
        SELECT id, delay_days, scheduled_time, interval_min, interval_max 
        FROM public.crm_followup_configs 
        WHERE user_id = '{$userIdStr}' AND stage_id = {$toStage} AND active = TRUE 
        LIMIT 1
      ");
      
      $followupConfig = $stF ? $stF->fetch(PDO::FETCH_ASSOC) : null;
      if ($stF) $stF->closeCursor();

      if ($followupConfig) {
        $configId = (int)$followupConfig['id'];
        $delayDays = (int)$followupConfig['delay_days'];
        $scheduledTime = $followupConfig['scheduled_time'];
        $intervalMin = (int)$followupConfig['interval_min'];
        $intervalMax = (int)$followupConfig['interval_max'];

        $baseTimestamp = strtotime("+{$delayDays} days");
        
        if ($scheduledTime) {
          list($hour, $minute) = explode(':', $scheduledTime);
          $baseTimestamp = strtotime(date('Y-m-d', $baseTimestamp) . " {$hour}:{$minute}:00");
        }
        
        $randomInterval = rand($intervalMin, $intervalMax);
        $scheduledAt = date('Y-m-d H:i:s', $baseTimestamp + $randomInterval);

        // Buscar telefone
        $leadPhone = '';
        if ($colPhone) {
          $stPhone = $pdo->query("SELECT {$colPhone} AS phone FROM {$schema}.{$table} WHERE {$colId} = {$leadId} LIMIT 1");
          if ($stPhone) {
            $phoneRow = $stPhone->fetch(PDO::FETCH_ASSOC);
            if ($phoneRow) $leadPhone = (string)($phoneRow['phone'] ?? '');
            $stPhone->closeCursor();
          }
        }

        // Inserir na fila
        $phoneEsc = $pdo->quote($leadPhone);
        $satEsc = $pdo->quote($scheduledAt);
        $userEsc = $pdo->quote($userIdStr);
        
$pdo->exec("
  INSERT INTO public.crm_followup_queue 
  (user_id, lead_id, config_id, scheduled_at, status, phone) 
  VALUES ({$userEsc}, {$leadId}, {$configId}, {$satEsc}, 'pending', {$phoneEsc})
");
      }

      $pdo->commit();
      jok();

    } catch (Exception $e) {
      $pdo->rollBack();
      jfail('Erro ao mover: ' . $e->getMessage(), 500);
    }
  }
  
  // ===== followup_get =====
  if ($action === 'followup_get') {
    $stageId = (int)($_GET['stage_id'] ?? 0);
    if (!$stageId) jfail('stage_id obrigatório', 400);

    $st = $pdo->prepare("
      SELECT 
        id, stage_id, message_type, message_text, media_url, 
        delay_days, scheduled_time, move_to_stage_id, move_stage_se_respondeu,
        interval_min, interval_max, active 
      FROM public.crm_followup_configs 
      WHERE user_id = :u AND stage_id = :sid 
      LIMIT 1
    ");
    $st->execute([':u' => $userIdStr, ':sid' => $stageId]);
    $config = $st->fetch(PDO::FETCH_ASSOC);
    $st->closeCursor();

    jok(['config' => $config ?: null]);
  }

  // ===== followup_queue =====
  if ($action === 'followup_queue') {
    $st = $pdo->prepare("
      SELECT 
        q.id, q.lead_id, q.scheduled_at, q.status, q.sent_at, q.error_message, q.created_at,
        c.stage_id, c.message_type, c.message_text, c.delay_days,
        l.company_name, l.phone
      FROM public.crm_followup_queue q
      JOIN public.crm_followup_configs c ON c.id = q.config_id
      LEFT JOIN public.leads l ON l.id = q.lead_id
      WHERE q.user_id = :u AND q.status = 'pending'
      ORDER BY q.scheduled_at ASC
      LIMIT 100
    ");
    $st->execute([':u' => $userIdStr]);
    $queue = $st->fetchAll(PDO::FETCH_ASSOC);
    $st->closeCursor();

    jok(['queue' => $queue, 'count' => count($queue), 'status' => 'success']);
  }

  // ===== followup_save =====
  if ($action === 'followup_save') {
    $stageId = (int)($_POST['stage_id'] ?? 0);
    if (!$stageId) jfail('stage_id obrigatório', 400);

    $chk = $pdo->prepare("SELECT 1 FROM public.crm_stages WHERE id=:id AND user_id=:u");
    $chk->execute([':id' => $stageId, ':u' => $userIdStr]);
    if (!$chk->fetchColumn()) jfail('Stage não encontrado', 404);
    $chk->closeCursor();

    $messageType = trim($_POST['message_type'] ?? 'text');
    if (!in_array($messageType, ['text', 'image', 'video', 'audio'])) jfail('message_type inválido', 400);

    $messageText = trim($_POST['message_text'] ?? '');
    $mediaUrl = trim($_POST['media_url'] ?? '');
    $delayDays = max(0, (int)($_POST['delay_days'] ?? 1));
    $scheduledTime = trim($_POST['scheduled_time'] ?? '');
    $moveToStageId = (int)($_POST['move_to_stage_id'] ?? 0) ?: null;
    $intervalMin = max(0, (int)($_POST['interval_min'] ?? 60));
    $intervalMax = max($intervalMin, (int)($_POST['interval_max'] ?? 300));
    $active = !empty($_POST['active']);

    if (in_array($messageType, ['image', 'video', 'audio']) && !$mediaUrl) {
      jfail('media_url obrigatório para image/video/audio', 400);
    }

    if ($scheduledTime && !preg_match('/^([01][0-9]|2[0-3]):[0-5][0-9]$/', $scheduledTime)) {
      jfail('Horário inválido. Use formato HH:MM (ex: 09:30)', 400);
    }

    $moveStageSeRespondeu = (int)($_POST['move_stage_se_respondeu'] ?? 0) ?: null;

    $sql = "
      INSERT INTO public.crm_followup_configs 
      (user_id, stage_id, message_type, message_text, media_url, delay_days, scheduled_time, move_to_stage_id, move_stage_se_respondeu, interval_min, interval_max, active) 
      VALUES (:u, :sid, :mt, :txt, :url, :dd, :stime, :move, :move_resp, :imin, :imax, :act) 
      ON CONFLICT (user_id, stage_id) 
      DO UPDATE SET 
        message_type = EXCLUDED.message_type, 
        message_text = EXCLUDED.message_text, 
        media_url = EXCLUDED.media_url, 
        delay_days = EXCLUDED.delay_days, 
        scheduled_time = EXCLUDED.scheduled_time, 
        move_to_stage_id = EXCLUDED.move_to_stage_id, 
        move_stage_se_respondeu = EXCLUDED.move_stage_se_respondeu,
        interval_min = EXCLUDED.interval_min, 
        interval_max = EXCLUDED.interval_max, 
        active = EXCLUDED.active, 
        updated_at = NOW() 
      RETURNING id
    ";

    $st = $pdo->prepare($sql);
    $st->execute([
      ':u' => $userIdStr, 
      ':sid' => $stageId, 
      ':mt' => $messageType, 
      ':txt' => $messageText, 
      ':url' => $mediaUrl, 
      ':dd' => $delayDays, 
      ':stime' => $scheduledTime ?: null, 
      ':move' => $moveToStageId, 
      ':move_resp' => $moveStageSeRespondeu,
      ':imin' => $intervalMin, 
      ':imax' => $intervalMax, 
      ':act' => $active ? 't' : 'f'
    ]);

    $configId = $st->fetch(PDO::FETCH_ASSOC)['id'] ?? 0;
    $st->closeCursor();
    jok(['config_id' => $configId]);
  }

  // ===== followup_delete =====
  if ($action === 'followup_delete') {
    $stageId = (int)($_POST['stage_id'] ?? 0);
    if (!$stageId) jfail('stage_id obrigatório', 400);

    $del = $pdo->prepare("DELETE FROM public.crm_followup_configs WHERE user_id = :u AND stage_id = :sid");
    $del->execute([':u' => $userIdStr, ':sid' => $stageId]);
    $del->closeCursor();
    jok();
  }

  // ===== followup_webhook =====
  if ($action === 'followup_webhook') {
    $now = date('Y-m-d H:i:s');
    $limit = date('Y-m-d H:i:s', time() + 300);

    $st = $pdo->prepare("SELECT q.id as queue_id, q.lead_id, q.scheduled_at, c.user_id, c.message_type, c.message_text, c.media_url, c.move_to_stage_id, l.phone, l.company_name as company FROM public.crm_followup_queue q JOIN public.crm_followup_configs c ON c.id = q.config_id LEFT JOIN public.leads l ON l.id = q.lead_id WHERE q.status = 'pending' AND q.scheduled_at <= :limit ORDER BY q.scheduled_at ASC LIMIT 50");
    $st->execute([':limit' => $limit]);
    $items = $st->fetchAll(PDO::FETCH_ASSOC);
    $st->closeCursor();

    jok(['items' => $items, 'count' => count($items)]);
  }

  // ===== followup_sent =====
  if ($action === 'followup_sent') {
    $queueId = (int)($_POST['queue_id'] ?? 0);
    $success = !empty($_POST['success']);
    $error = trim($_POST['error'] ?? '');

    if (!$queueId) jfail('queue_id obrigatório', 400);

    if ($success) {
      $upd = $pdo->prepare("UPDATE public.crm_followup_queue SET status = 'sent', sent_at = NOW() WHERE id = :id");
      $upd->execute([':id' => $queueId]);
      $upd->closeCursor();
    } else {
      $upd = $pdo->prepare("UPDATE public.crm_followup_queue SET status = 'failed', error_message = :err WHERE id = :id");
      $upd->execute([':id' => $queueId, ':err' => $error]);
      $upd->closeCursor();
    }

    jok();
  }

  // ===== followup_move_after_send =====
  if ($action === 'followup_move_after_send') {
    $leadId = (int)($_POST['lead_id'] ?? 0);
    $toStageId = (int)($_POST['to_stage_id'] ?? 0);

    if (!$leadId || !$toStageId) jfail('Dados inválidos', 400);

    $cfg = detectLeadsSchema($pdo);
    extract($cfg);

    if (!$colStageId) jfail('stage_id não existe na tabela leads', 500);

    $sql = "UPDATE {$schema}.{$table} SET {$colStageId} = :sid WHERE {$colId} = :lid";
    $params = [':sid' => $toStageId, ':lid' => $leadId];

    if ($colUserId) {
      $userRow = $pdo->prepare("SELECT {$colUserId} as user_id FROM {$schema}.{$table} WHERE {$colId} = :lid LIMIT 1");
      $userRow->execute([':lid' => $leadId]);
      $leadUser = $userRow->fetch(PDO::FETCH_ASSOC)['user_id'] ?? null;
      $userRow->closeCursor();
      
      if ($leadUser) {
        $t = colType($pdo, $schema, $table, $colUserId) ?: 'text';
        if ($t === 'uuid') { $sql .= " AND {$colUserId} = :u::uuid"; }
        else { $sql .= " AND {$colUserId} = :u"; }
        $params[':u'] = $leadUser;
      }
    }

    $upd = $pdo->prepare($sql);
    $upd->execute($params);
    $upd->closeCursor();
    jok();
  }

  jfail('Ação inválida', 404);
  
} catch (Throwable $e) {
  jfail('Erro: '.$e->getMessage(), 500);
}