<?php
// public/api/disparos_scheduler.php
require_once __DIR__ . '/../../app/config.php';
require_once __DIR__ . '/../../app/db.php';
require_once __DIR__ . '/../../app/helpers.php'; // http_post_json()

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

// ---- Config de logs básicos ----
ini_set('display_errors', 0); // não mostrar erro pro usuário
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/disparos_scheduler_error.log');
error_reporting(E_ALL);

const SCHED_RUN_LOG = __DIR__ . '/disparos_scheduler_run.log';

function slog(string $msg): void {
    file_put_contents(
        SCHED_RUN_LOG,
        date('c') . ' - ' . $msg . PHP_EOL,
        FILE_APPEND
    );
}

function jexit($p, $s=200){
  http_response_code($s);
  echo json_encode($p, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
  exit;
}

try {

  slog('INÍCIO');

  // Proteção simples por chave
  if (defined('CRON_KEY') && CRON_KEY !== '') {
    if (!isset($_GET['key']) || $_GET['key'] !== CRON_KEY) {
      slog('KEY INVÁLIDA');
      jexit(['ok'=>false,'error'=>'forbidden'], 403);
    }
  }

  $pdo = db();

  /** ---------- Helpers compartilhados ---------- */

  /** Carrega campanha + user + webhook + funil (igual à API de disparos). */
  function loadCampaignWithUser(PDO $pdo, int $disparoId, string $userId): array {
    // campanha
    $stC = $pdo->prepare("
      SELECT id, user_id, nome, min_delay, max_delay, total, enviados, erros, created_at, leads,
             COALESCE(ignore_default_message,false) AS ignore_default_message,
             COALESCE(ignore_timeout_seconds,0)     AS ignore_timeout_seconds,
             status, started_webhook_at, paused_webhook_at,
             funil_id, funil_snapshot
        FROM disparos
       WHERE id=:id AND user_id=:u
       LIMIT 1
    ");
    $stC->execute([':id'=>$disparoId, ':u'=>$userId]);
    $camp = $stC->fetch(PDO::FETCH_ASSOC);
    if (!$camp) throw new RuntimeException('Campanha não encontrada.');

    // leads payload (snapshot)
    $leadsPayload = [];
    if (!empty($camp['leads'])) {
      $leadsPayload = is_string($camp['leads'])
        ? (json_decode($camp['leads'], true) ?: [])
        : (is_array($camp['leads']) ? $camp['leads'] : []);
    }

    // user
    $stU = $pdo->prepare("
      SELECT id, email, name, meta, prompt_broadcast_agent, default_broadcast_message
      FROM users
      WHERE id=:u
      LIMIT 1
    ");
    $stU->execute([':u'=>$userId]);
    $userRow  = $stU->fetch(PDO::FETCH_ASSOC) ?: [];
    $userMeta = $userRow['meta'] ?? null;
    if (is_string($userMeta)) $userMeta = json_decode($userMeta, true);
    if (!is_array($userMeta)) $userMeta = [];

    // webhook
    $webhookList = $userMeta['webhook'] ?? [];
    $webhookUrl  = (defined('N8N_WEBHOOK_URL') && N8N_WEBHOOK_URL) ? N8N_WEBHOOK_URL : ($webhookList[0] ?? '');

    // funil snapshot (congelado na criação/início)
    $funil = null;
    if (!empty($camp['funil_snapshot'])) {
      $funil = is_string($camp['funil_snapshot'])
              ? (json_decode($camp['funil_snapshot'], true) ?: null)
              : (is_array($camp['funil_snapshot']) ? $camp['funil_snapshot'] : null);
    }

    return [
      'camp'        => $camp,
      'leads'       => $leadsPayload,
      'userRow'     => $userRow,
      'userMeta'    => $userMeta,
      'webhookUrl'  => $webhookUrl,
      'funil'       => $funil,
    ];
  }

  /** Monta payload padrão do webhook (igual ao start/resume). */
  function buildCampaignPayload(array $bundle): array {
    $camp     = $bundle['camp'];
    $userRow  = $bundle['userRow'];
    $userMeta = $bundle['userMeta'];
    $funil    = $bundle['funil'];
    $leads    = $bundle['leads'];

    return [
      'event'    => 'campaign.started',
      'campaign' => [
        'id'        => (int)$camp['id'],
        'name'      => $camp['nome'],
        'min_delay' => (int)$camp['min_delay'],
        'max_delay' => (int)$camp['max_delay'],
        'total'     => (int)$camp['total'],
        'status'    => 'running',
        'created_at'=> $camp['created_at'],
        'ignore_default_message' => (bool)$camp['ignore_default_message'],
        'ignore_timeout_seconds' => (int)$camp['ignore_timeout_seconds'],
      ],
      'user' => [
        'id'    => $userRow['id'] ?? null,
        'email' => $userRow['email'] ?? null,
        'name'  => $userRow['name'] ?? null,
      ],
      'meta'                     => $userMeta,
      'instance'                 => $userMeta['instance'] ?? null,
      'prompt_broadcast_agent'   => trim((string)($userRow['prompt_broadcast_agent'] ?? '')),
      'default_broadcast_message'=> trim((string)($userRow['default_broadcast_message'] ?? '')),
      'funnel'                   => $funil,
      'leads'                    => $leads,
    ];
  }

  /** Envia webhook se houver URL. */
  function sendCampaignWebhook(?string $webhookUrl, array $payload): ?array {
    if (!$webhookUrl) return null;
    return http_post_json($webhookUrl, $payload);
  }

  /** Janela por horário (suporta atravessar meia-noite). */
  function isWithinWindow(array $schedule, DateTimeImmutable $nowLocal): bool {
    $start = $schedule['start'] ?? null; // "HH:MM"
    $end   = $schedule['end']   ?? null;
    if (!$start || !$end) return true;

    [$sh,$sm] = array_map('intval', explode(':', $start));
    [$eh,$em] = array_map('intval', explode(':', $end));

    $today   = $nowLocal->format('Y-m-d');
    $startAt = new DateTimeImmutable("$today $sh:$sm:00", $nowLocal->getTimezone());
    $endAt   = new DateTimeImmutable("$today $eh:$em:00", $nowLocal->getTimezone());

    if ($startAt->getTimestamp() === $endAt->getTimestamp()) return true; // 24h aberto

    if ($startAt < $endAt) { // mesma data
      return ($nowLocal >= $startAt && $nowLocal <= $endAt);
    } else { // atravessa meia-noite
      return ($nowLocal >= $startAt) || ($nowLocal <= $endAt);
    }
  }

  /** Bloqueios por weekday (0=Dom...6=Sáb) e por datas (YYYY-MM-DD). */
  function isBlocked(array $schedule, DateTimeImmutable $nowLocal): bool {
    $w = (int)$nowLocal->format('w');
    $blockedWeekdays = array_map('intval', (array)($schedule['blocked_weekdays'] ?? []));
    if (in_array($w, $blockedWeekdays, true)) return true;

    $today = $nowLocal->format('Y-m-d');
    $blockedDates = (array)($schedule['blocked_dates'] ?? []);
    if (in_array($today, $blockedDates, true)) return true;

    return false;
  }

  /** Pausa campanha + itens (espelha o botão Pause da API). */
  function auto_pause_campaign(PDO $pdo, int $id, string $userId): void {
    $pdo->beginTransaction();
    try {
      // Campanha -> paused (auto_paused)
      $u = $pdo->prepare("
        UPDATE disparos
           SET status='paused',
               paused_webhook_at = NOW(),
               auto_paused = true
         WHERE id=:id AND user_id=:u
           AND status IN ('running','paused','draft')
      ");
      $u->execute([':id'=>$id, ':u'=>$userId]);

      // Itens em fluxo -> paused
      $upItems = $pdo->prepare("
        UPDATE disparos_itens
           SET status='paused'
         WHERE disparo_id=:d
           AND status IN ('pending','queued','retry')
      ");
      $upItems->execute([':d'=>$id]);

      $pdo->commit();
    } catch (Throwable $e) {
      $pdo->rollBack();
      throw $e;
    }
  }

  /** Retoma campanha + itens (espelha o botão Resume da API). */
  function auto_resume_campaign(PDO $pdo, int $id, string $userId): void {
    $pdo->beginTransaction();
    try {
      // Campanha -> running (limpa auto_paused)
      $u = $pdo->prepare("
        UPDATE disparos
           SET status='running',
               paused_webhook_at = NULL,
               auto_paused = false
         WHERE id=:id AND user_id=:u
           AND status='paused'
      ");
      $u->execute([':id'=>$id, ':u'=>$userId]);

      // Itens pausados -> pending
      $upItems = $pdo->prepare("
        UPDATE disparos_itens
           SET status='pending'
         WHERE disparo_id=:d
           AND status='paused'
      ");
      $upItems->execute([':d'=>$id]);

      $pdo->commit();
    } catch (Throwable $e) {
      $pdo->rollBack();
      throw $e;
    }
  }

  /** ---------- Loop principal do scheduler ---------- */

  $nowUtc = new DateTimeImmutable('now', new DateTimeZone('UTC'));

  // Seleciona campanhas com schedule ativo e em estados relevantes
  $st = $pdo->query("
    SELECT d.id, d.user_id, d.status, d.auto_paused, d.schedule::text AS schedule_text
      FROM disparos d
     WHERE d.schedule IS NOT NULL
       AND d.status IN ('running','paused')
  ");
  $rows = $st->fetchAll(PDO::FETCH_ASSOC);

  slog('Campanhas encontradas: ' . count($rows));

  $changes = ['paused'=>[], 'resumed'=>[]];

  foreach ($rows as $r) {
    $sch = json_decode($r['schedule_text'] ?? 'null', true) ?: null;
    if (!$sch) continue;

    // Timezone local da campanha
    $tzId = $sch['tz'] ?? 'America/Sao_Paulo';
    try { $tz = new DateTimeZone($tzId); } catch(Throwable $e){ $tz = new DateTimeZone('America/Sao_Paulo'); }
    $nowLocal = $nowUtc->setTimezone($tz);

    $inside  = isWithinWindow($sch, $nowLocal);
    $blocked = isBlocked($sch, $nowLocal);

    if (!$inside || $blocked) {
      // Fora de janela ou dia/data bloqueada -> AUTO PAUSE
      if ($r['status'] === 'running') {
        slog("Auto pause campanha {$r['id']} (user {$r['user_id']})");
        auto_pause_campaign($pdo, (int)$r['id'], $r['user_id']);
        $changes['paused'][] = (int)$r['id'];
      }
      continue;
    }

    // Dentro da janela e não bloqueado -> AUTO RESUME (se o robô pausou)
    if ($r['status'] === 'paused' && !empty($r['auto_paused'])) {
      slog("Auto resume campanha {$r['id']} (user {$r['user_id']})");
      auto_resume_campaign($pdo, (int)$r['id'], $r['user_id']);

      // Reenvia o webhook COMPLETO após retomar + desbloquear itens
      try {
        $bundle  = loadCampaignWithUser($pdo, (int)$r['id'], $r['user_id']);
        $payload = buildCampaignPayload($bundle);
        sendCampaignWebhook($bundle['webhookUrl'], $payload);
      } catch (Throwable $e) {
        slog("Erro ao enviar webhook da campanha {$r['id']}: " . $e->getMessage());
        // segue o baile, não derruba o scheduler
      }

      $changes['resumed'][] = (int)$r['id'];
    }
  }

  // (Opcional) Debug pontual: avaliar uma campanha sem alterar nada
  if (isset($_GET['debug_id'])) {
    $id = (int)$_GET['debug_id'];
    $st1 = $pdo->prepare("SELECT id, user_id, status, auto_paused, schedule::text AS schedule_text FROM disparos WHERE id=:id LIMIT 1");
    $st1->execute([':id'=>$id]);
    $r1 = $st1->fetch(PDO::FETCH_ASSOC);
    if ($r1) {
      $sch1 = json_decode($r1['schedule_text'] ?? 'null', true) ?: null;
      $tzId1 = $sch1['tz'] ?? 'America/Sao_Paulo';
      try { $tz1 = new DateTimeZone($tzId1); } catch(Throwable $e){ $tz1 = new DateTimeZone('America/Sao_Paulo'); }
      $nowLocal1 = $nowUtc->setTimezone($tz1);
      $inside1 = $sch1 ? isWithinWindow($sch1, $nowLocal1) : true;
      $blocked1= $sch1 ? isBlocked($sch1, $nowLocal1)     : false;

      slog("DEBUG campanha {$id}");
      jexit([
        'ok'=>true,
        'debug'=>[
          'id'=>$id,
          'now_local'=>$nowLocal1->format('Y-m-d H:i:s T'),
          'inside_window'=>$inside1,
          'blocked'=>$blocked1,
          'weekday'=>$nowLocal1->format('w'),
          'schedule'=>$sch1,
          'status'=>$r1['status'],
          'auto_paused'=>(bool)$r1['auto_paused']
        ],
        'changes'=>$changes
      ]);
    }
  }

  slog('FIM OK');
  jexit(['ok'=>true, 'changes'=>$changes]);

} catch (Throwable $e) {
  // Qualquer erro não tratado cai aqui
  slog('ERRO FATAL: ' . $e->getMessage());
  jexit([
    'ok'    => false,
    'error' => $e->getMessage(),
    // opcional: ajuda pra debug rápido
    'type'  => get_class($e)
  ], 500);
}
