<?php
// Depto de Engenharia e Automação - Master Automação - Autor: Derlivon Silva
// Dashboard central de backups com DETALHES expansíveis por linha.

// Horario de Brasilia em TODA data exibida (o servidor 24h roda em UTC — sem isso
// as datas apareciam 3h adiantadas).
date_default_timezone_set('America/Sao_Paulo');

$cfgFile = __DIR__ . '/config.php';
if (!is_file($cfgFile)) {
    echo '<h1>config.php não existe</h1>'; exit;
}
$cfg = include $cfgFile;

session_start();
if (!empty($_POST['senha']) && hash_equals((string)($cfg['senha_admin'] ?? ''), (string)$_POST['senha'])) {
    $_SESSION['painel_logado'] = true;
}
if (!empty($_GET['sair'])) { session_destroy(); header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?')); exit; }
if (empty($_SESSION['painel_logado'])) {
    echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>Painel Central de Backups — Master Automação</title>
    <style>body{font-family:system-ui;background:#0f172a;color:#fff;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0}
    .card{background:#1e293b;padding:2rem;border-radius:12px;max-width:380px;width:90%}
    h1{margin:0 0 1rem;font-size:1.4rem}
    input{width:100%;padding:.7rem;border-radius:6px;border:1px solid #475569;background:#0f172a;color:#fff;box-sizing:border-box}
    button{margin-top:.7rem;width:100%;padding:.7rem;background:#6366f1;color:#fff;border:none;border-radius:6px;font-weight:700;cursor:pointer}</style>
    </head><body><div class="card">
    <h1>🛡️ Painel Central de Backups — Master Automação</h1>
    <form method="post"><input type="password" name="senha" placeholder="Senha admin" autofocus required><button>Entrar</button></form>
    </div></body></html>';
    exit;
}

$dirStorage = __DIR__ . '/storage';
$clientes = [];
foreach (glob($dirStorage . '/*.json') ?: [] as $f) {
    $j = @json_decode((string)@file_get_contents($f), true);
    if (is_array($j)) {
        $j['_arquivo_json'] = basename($f);
        $clientes[] = $j;
    }
}

// Enriquece com versao_bi vinda do log de atualizacao. Quando cliente atualiza
// via OTA, o Logger manda pra storage/atualizacoes/<host>.jsonl com versao_nova
// preenchida. Sem isso, painel so mostraria versao atualizada no PROXIMO backup
// (delay de ate 24h). Agora aparece na hora.
$dirAtu = $dirStorage . '/atualizacoes';
if (is_dir($dirAtu)) {
    // Cache: mapa host -> ['versao'=>X, 'em'=>timestamp]
    $ultAtualizacao = [];
    foreach (glob($dirAtu . '/*.jsonl') ?: [] as $fAtu) {
        $host = basename($fAtu, '.jsonl');
        // Le so a ULTIMA linha do arquivo (mais recente)
        $linhas = @file($fAtu, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES) ?: [];
        for ($i = count($linhas) - 1; $i >= 0; $i--) {
            $reg = @json_decode($linhas[$i], true);
            if (is_array($reg) && !empty($reg['sucesso']) && !empty($reg['versao_nova'])) {
                $ultAtualizacao[$host] = [
                    'versao' => (string)$reg['versao_nova'],
                    'em'     => strtotime((string)($reg['gravado_em'] ?? 'now')),
                ];
                break;
            }
        }
    }
    // Aplica sobreposicao: se ultima atualizacao eh MAIS RECENTE que o heartbeat
    // de backup, usa a versao dela pra exibir no painel.
    foreach ($clientes as &$c) {
        $host = (string)($c['host_cliente'] ?? '');
        if ($host === '' || !isset($ultAtualizacao[$host])) continue;
        $emBackup = strtotime((string)($c['em'] ?? '2000-01-01'));
        if ($ultAtualizacao[$host]['em'] > $emBackup) {
            $c['versao_bi']  = $ultAtualizacao[$host]['versao'];
            $c['_atualizado_via_log'] = true;  // sinaliza pra UI mostrar hint
        }
    }
    unset($c);
}

// Calcula limite por cliente: backup diario vira ATRASADO em 28h,
// 12h em 14h, semanal em 8 dias, etc. Cliente sem dica de frequencia
// nos metadados usa 28h como default (cobre o caso comum diario).
function _limiteAtrasoSeg(array $c): int {
    $intervalo = (int)($c['intervalo_esperado_horas'] ?? 0);
    if ($intervalo > 0) return ($intervalo + 4) * 3600;  // +4h tolerancia
    return 28 * 3600;                                      // default diario + tolerancia
}

// Classificacao por linha (centralizada — usada pelo KPI e pela renderizacao):
//   em_andamento: status='em_andamento' E recebimento < 90s          (bolinha pulsante)
//   travado    : status='em_andamento' E recebimento > 5min         (sem heartbeat ha tempo)
//   falha      : status='falha'
//   atraso     : ultimo recebimento > limite (intervalo_esperado_horas + 4h)
//   ok         : caso contrario
function _classificar(array $c): array {
    $em = strtotime((string)($c['em'] ?? 'now'));
    $idade = time() - $em;
    $status = (string)($c['status'] ?? '');
    if ($status === 'em_andamento') {
        if ($idade <= 90)   return ['em_andamento', 'EM ANDAMENTO'];
        if ($idade > 300)   return ['travado',      'POSSIVELMENTE TRAVADO'];
        return ['em_andamento', 'EM ANDAMENTO'];
    }
    if ($status === 'falha')                       return ['falha', 'FALHA'];
    if ($idade > _limiteAtrasoSeg($c))             return ['atraso', 'ATRASADO'];
    return ['ok', 'OK'];
}

// Suporte a deletar registro órfão (cliente sem nome, payload incompleto).
// Usado pelo botão "🗑 Limpar" que aparece em linhas inválidas no painel.
if (isset($_GET['limpar']) && is_string($_GET['limpar'])) {
    $alvo = preg_replace('/[^a-zA-Z0-9_-]/', '', basename($_GET['limpar'], '.json'));
    if ($alvo !== '') {
        $arqLimpar = __DIR__ . '/storage/' . $alvo . '.json';
        if (is_file($arqLimpar)) @unlink($arqLimpar);
    }
    header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
    exit;
}
// Limpar TODOS órfãos de uma vez (cliente vazio, "?", ou job_id vazio no JSON)
if (isset($_GET['limpar_todos_orfaos'])) {
    $n = 0;
    foreach (glob(__DIR__ . '/storage/*.json') ?: [] as $f) {
        $j = @json_decode((string)@file_get_contents($f), true);
        if (!is_array($j)) continue;
        $cli = trim((string)($j['cliente'] ?? ''));
        $job = trim((string)($j['job_id'] ?? ''));
        if ($cli === '' || $cli === '?' || $job === '' || $job === '?') {
            if (@unlink($f)) $n++;
        }
    }
    header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?') . '?limpou=' . $n);
    exit;
}
// Excluir cliente inteiro (qualquer, não só órfão) — botão "Excluir permanente"
if (isset($_GET['excluir']) && is_string($_GET['excluir'])) {
    $alvo = preg_replace('/[^a-zA-Z0-9_-]/', '', basename($_GET['excluir'], '.json'));
    if ($alvo !== '') {
        $arqExcluir = __DIR__ . '/storage/' . $alvo . '.json';
        if (is_file($arqExcluir)) @unlink($arqExcluir);
    }
    header('Location: ' . strtok($_SERVER['REQUEST_URI'], '?'));
    exit;
}

$totOk = $totFalha = $totAtraso = $totAndamento = $totDesatualizados = 0;
$verPublicada = versaoMaisRecentePublicada();
foreach ($clientes as $c) {
    [$cls,] = _classificar($c);
    if      ($cls === 'falha')                       $totFalha++;
    elseif  ($cls === 'atraso' || $cls === 'travado') $totAtraso++;
    elseif  ($cls === 'em_andamento')                $totAndamento++;
    else                                             $totOk++;
    if (precisaAtualizar((string)($c['versao_bi'] ?? ''), $verPublicada)) $totDesatualizados++;
}
usort($clientes, function ($a, $b) {
    // Em andamento sobe pro topo (gestor esta acompanhando agora).
    $aStatus = (string)($a['status'] ?? '');
    $bStatus = (string)($b['status'] ?? '');
    $aAnd = ($aStatus === 'em_andamento') ? 1 : 0;
    $bAnd = ($bStatus === 'em_andamento') ? 1 : 0;
    if ($aAnd !== $bAnd) return $bAnd - $aAnd;
    $em_a = strtotime((string)($a['em'] ?? 'now'));
    $em_b = strtotime((string)($b['em'] ?? 'now'));
    return $em_b - $em_a;  // depois: mais recente primeiro
});

// Versao mais recente publicada (lida do versao.json do servidor 24h, com cache de 5 min).
// Tenta primeiro o arquivo local do Apache (`C:\Fire-Updates\public\atualizacoes\`),
// depois cai pra HTTP localhost. Usado pra marcar clientes desatualizados na tabela.
function versaoMaisRecentePublicada(): string {
    static $cache = null;
    if ($cache !== null) return $cache;
    $cacheFile = __DIR__ . '/storage/_cache/versao_publicada.json';
    if (is_file($cacheFile) && (time() - @filemtime($cacheFile)) < 300) {
        $j = @json_decode((string)@file_get_contents($cacheFile), true);
        if (is_array($j) && !empty($j['versao'])) return $cache = (string)$j['versao'];
    }
    $candidatos = [
        'C:/Fire-Updates/public/atualizacoes/versao.json',
        'C:\\Fire-Updates\\public\\atualizacoes\\versao.json',
    ];
    foreach ($candidatos as $p) {
        if (is_file($p)) {
            $j = @json_decode((string)@file_get_contents($p), true);
            if (is_array($j) && !empty($j['versao'])) {
                @mkdir(dirname($cacheFile), 0777, true);
                @file_put_contents($cacheFile, json_encode($j));
                return $cache = (string)$j['versao'];
            }
        }
    }
    // Fallback: HTTP local
    $ctx = stream_context_create(['http' => ['timeout' => 3]]);
    $raw = @file_get_contents('http://localhost:8084/atualizacoes/versao.json', false, $ctx);
    if ($raw !== false) {
        $j = @json_decode($raw, true);
        if (is_array($j) && !empty($j['versao'])) {
            @mkdir(dirname($cacheFile), 0777, true);
            @file_put_contents($cacheFile, json_encode($j));
            return $cache = (string)$j['versao'];
        }
    }
    return $cache = '';
}

function precisaAtualizar(string $verCliente, string $verPublicada): bool {
    if ($verCliente === '' || $verCliente === '?' || $verPublicada === '') return false;
    return version_compare($verCliente, $verPublicada, '<');
}

function fmtMB(int $b): string {
    if ($b < 1024)              return $b . ' B';
    if ($b < 1048576)           return number_format($b/1024, 0, ',', '.') . ' KB';
    if ($b < 1073741824)        return number_format($b/1048576, 1, ',', '.') . ' MB';
    return number_format($b/1073741824, 2, ',', '.') . ' GB';
}
function ago(int $em): string {
    $d = time() - $em;
    if ($d < 60) return 'agora';
    if ($d < 3600) return floor($d/60) . ' min atrás';
    if ($d < 86400) return floor($d/3600) . 'h atrás';
    return floor($d/86400) . 'd atrás';
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Painel Central de Backups — Master Automação</title>
<?php
// Refresh dinamico: se ha backup rodando, atualiza a cada 15s pra acompanhar
// progresso; senao, 60s (modo normal de monitoramento).
$refresh = ($totAndamento > 0) ? 15 : 60;
?>
<meta http-equiv="refresh" content="<?= $refresh ?>">
<style>
body { font-family: system-ui, -apple-system, sans-serif; background: #f8fafc; color: #0f172a; margin: 0; padding: 1.5rem; }
h1 { margin: 0 0 1rem; font-size: 1.5rem; }
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: .7rem; margin-bottom: 1.5rem; }
.kpi { background: #fff; border-left: 4px solid #94a3b8; border-radius: 8px; padding: .9rem; box-shadow: 0 1px 3px rgba(0,0,0,.05); }
.kpi.ok { border-color: #16a34a; background: #dcfce7; }
.kpi.falha { border-color: #dc2626; background: #fee2e2; }
.kpi.atraso { border-color: #f59e0b; background: #fef3c7; }
.kpi.andamento { border-color: #2563eb; background: #dbeafe; }
.kpi .lbl { font-size: .78rem; font-weight: 700; opacity: .8; }
.kpi .num { font-size: 2rem; font-weight: 800; }
table { width: 100%; border-collapse: collapse; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 3px rgba(0,0,0,.05); }
th, td { padding: .6rem .8rem; text-align: left; font-size: .87rem; border-top: 1px solid #f1f5f9; }
th { background: #f8fafc; font-size: .72rem; color: #475569; text-transform: uppercase; }
tr.linha { cursor: pointer; }
tr.linha:hover { background: #f8fafc; }
tr.detalhes td { background: #fafafa; padding: 1rem; }
tr.detalhes pre { background: #1e293b; color: #e2e8f0; padding: 1rem; border-radius: 6px; overflow-x: auto; font-size: .78rem; line-height: 1.5; }
.tag { display: inline-block; padding: .2rem .65rem; border-radius: 12px; font-size: .72rem; font-weight: 800; }
.tag.ok    { background: #dcfce7; color: #166534; }
.tag.falha { background: #fee2e2; color: #991b1b; }
.tag.atraso{ background: #fef3c7; color: #854d0e; }
.tag.em_andamento { background: #dbeafe; color: #1d4ed8; }
.tag.travado      { background: #fed7aa; color: #9a3412; }
.tag.orfao        { background: #f3e8ff; color: #6b21a8; }
tr.linha.orfao    { background: #faf5ff; opacity: .75; }
tr.linha.orfao:hover { background: #f5f3ff; }
/* Bolinha azul pulsante — backup em andamento (heartbeat fresco) */
.bk-pulse {
  display: inline-block; width: 12px; height: 12px; border-radius: 50%;
  background: #2563eb; margin-right: .5rem; vertical-align: middle;
  animation: bk-pulse 1.4s infinite ease-in-out;
  box-shadow: 0 0 0 0 rgba(37,99,235,.7);
}
@keyframes bk-pulse {
  0%   { transform: scale(1);   box-shadow: 0 0 0 0   rgba(37,99,235,.7); }
  50%  { transform: scale(1.3); box-shadow: 0 0 0 8px rgba(37,99,235,0); }
  100% { transform: scale(1);   box-shadow: 0 0 0 0   rgba(37,99,235,0); }
}
.bk-travado-warn {
  background:#fff7ed;border:1px solid #fb923c;border-radius:6px;
  padding:.6rem .8rem;font-size:.82rem;color:#9a3412;margin-top:.6rem
}
.detalhe-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 1rem; margin-bottom: 1rem; }
.detalhe-grid div { background: #fff; padding: .7rem 1rem; border-radius: 6px; border: 1px solid #e2e8f0; }
.detalhe-grid div small { color: #64748b; font-size: .72rem; font-weight: 700; text-transform: uppercase; display: block; margin-bottom: .2rem; }
.detalhe-grid div strong { font-size: .9rem; word-break: break-all; }
.erro-bloco { background: #fee2e2; color: #991b1b; padding: .7rem 1rem; border-radius: 6px; font-family: monospace; font-size: .8rem; margin-bottom: 1rem; word-break: break-word; white-space: pre-wrap; }
</style>
</head>
<body>

<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem">
  <h1>🛡️ Painel Central de Backups — <span style="color:#6366f1">Master Automação</span></h1>
  <div><small style="color:#94a3b8">atualiza a cada 60s · clique numa linha pra ver detalhes</small> · <a href="uso.php" style="color:#6366f1">📈 Uso dos clientes</a> · <a href="atualizacoes.php" style="color:#10b981">🔄 Atualizações OTA</a> · <a href="config-email.php" style="color:#f97316">🔔 Email</a> · <a href="?sair=1" style="color:#6366f1">Sair</a></div>
</div>

<div class="cards">
  <div class="kpi ok"><div class="lbl">✅ EM DIA</div><div class="num"><?= $totOk ?></div></div>
  <div class="kpi falha"><div class="lbl">❌ FALHA</div><div class="num"><?= $totFalha ?></div></div>
  <div class="kpi atraso"><div class="lbl">⚠ ATRASADO</div><div class="num"><?= $totAtraso ?></div></div>
  <?php if ($totAndamento > 0): ?>
    <div class="kpi andamento"><div class="lbl">🔄 EM ANDAMENTO</div><div class="num"><?= $totAndamento ?></div></div>
  <?php endif; ?>
  <?php if ($totDesatualizados > 0): ?>
    <div class="kpi" style="border-color:#f59e0b;background:#fffbeb">
      <div class="lbl" style="color:#92400e">⬆ DESATUALIZADOS</div>
      <div class="num" style="color:#b45309"><?= $totDesatualizados ?></div>
    </div>
  <?php endif; ?>
  <?php if ($verPublicada !== ''): ?>
    <div class="kpi" style="border-color:#6366f1;background:#eef2ff">
      <div class="lbl" style="color:#3730a3">📦 ÚLTIMA PUBLICADA</div>
      <div class="num" style="color:#4338ca;font-size:1.4rem">v<?= htmlspecialchars($verPublicada) ?></div>
    </div>
  <?php endif; ?>
  <div class="kpi"><div class="lbl">📊 TOTAL</div><div class="num"><?= count($clientes) ?></div></div>
</div>

<table>
  <thead>
    <tr>
      <th></th>
      <th>Cliente</th>
      <th>Status</th>
      <th>Versão</th>
      <th>Último</th>
      <th>Tamanho</th>
      <th>Modo</th>
      <th>Máquina</th>
      <th>IP</th>
    </tr>
  </thead>
  <tbody>
    <?php if (empty($clientes)): ?>
      <tr><td colspan="9" style="text-align:center;padding:3rem;color:#94a3b8">Nenhum cliente reportou ainda.</td></tr>
    <?php endif; ?>
    <?php foreach ($clientes as $i => $c):
      $em = strtotime((string)($c['em'] ?? 'now'));
      [$cls, $lbl] = _classificar($c);
      $emAndamento = ($cls === 'em_andamento' || $cls === 'travado');
      $tamMostrar = $emAndamento ? (int)($c['tamanho_atual'] ?? 0) : (int)($c['tamanho'] ?? 0);
      // Detecta payload órfão / inválido: cliente em branco, "?", ou job_id ausente.
      // Esses registros não deveriam existir (api.php valida) mas podem ser lixo antigo.
      $cliRaw  = trim((string)($c['cliente'] ?? ''));
      $jobRaw  = trim((string)($c['job_id']  ?? ''));
      $orfao   = ($cliRaw === '' || $cliRaw === '?' || $jobRaw === '' || $jobRaw === '?');
      if ($orfao) { $cls = 'orfao'; $lbl = '⚠ ÓRFÃO'; }
    ?>
      <tr class="linha<?= $orfao ? ' orfao' : '' ?>" onclick="document.getElementById('d<?= $i ?>').style.display = document.getElementById('d<?= $i ?>').style.display === 'table-row' ? 'none' : 'table-row'">
        <td><?= $emAndamento ? '<span class="bk-pulse" title="Backup rodando agora"></span>' : '▸' ?></td>
        <td>
          <strong><?= htmlspecialchars($c['cliente'] ?? '?') ?></strong>
          <?php if ($orfao && !empty($c['_arquivo_json'])): ?>
            <a href="?limpar=<?= urlencode($c['_arquivo_json']) ?>"
               onclick="event.stopPropagation(); return confirm('Apagar este registro órfão (cliente sem nome)?')"
               title="Apagar registro órfão do storage"
               style="margin-left:.5rem;color:#dc2626;text-decoration:none;font-size:.78rem">🗑 Limpar</a>
          <?php endif; ?>
        </td>
        <td>
          <span class="tag <?= $cls ?>"><?= $lbl ?></span>
          <?php if ($emAndamento && !empty($c['estagio'])): ?>
            <div style="font-size:.7rem;color:#1d4ed8;margin-top:.2rem">
              <?= htmlspecialchars($c['estagio']) ?>
            </div>
          <?php endif; ?>
        </td>
        <td>
          <?php
            $verCli = (string)($c['versao_bi'] ?? '');
            $desatualizado = precisaAtualizar($verCli, $verPublicada);
          ?>
          <?php if ($verCli === '' || $verCli === '?'): ?>
            <span style="color:#94a3b8;font-family:monospace">—</span>
          <?php else: ?>
            <strong style="font-family:monospace;font-size:.85rem"><?= htmlspecialchars($verCli) ?></strong>
            <?php if (!empty($c['_atualizado_via_log'])): ?>
              <span title="Versão vem do log de atualização (ainda não rodou backup nessa versão)" style="color:#6366f1;font-size:.75rem;margin-left:.2rem">🆕</span>
            <?php endif; ?>
          <?php endif; ?>
          <?php if ($desatualizado): ?>
            <div style="margin-top:.2rem">
              <span style="background:#fef3c7;color:#92400e;padding:.1rem .4rem;border-radius:4px;font-size:.7rem;font-weight:700">
                ⬆ ATUALIZAR p/ v<?= htmlspecialchars($verPublicada) ?>
              </span>
            </div>
          <?php elseif ($verCli !== '' && $verCli !== '?' && $verPublicada !== ''): ?>
            <div style="margin-top:.15rem;font-size:.65rem;color:#16a34a">✓ atualizado</div>
          <?php endif; ?>
        </td>
        <td>
          <?= date('d/m/Y H:i', $em) ?><br>
          <small style="color:#94a3b8"><?= ago($em) ?></small>
          <?php if ($emAndamento && !empty($c['tempo_decorrido'])):
            $tdMin = floor((int)$c['tempo_decorrido']/60); $tdSec = (int)$c['tempo_decorrido']%60; ?>
            <br><small style="color:#1d4ed8;font-weight:700">⏱ <?= $tdMin ?>min <?= $tdSec ?>s rodando</small>
          <?php endif; ?>
        </td>
        <td>
          <?= fmtMB($tamMostrar) ?>
          <?php if ($emAndamento): ?><br><small style="color:#1d4ed8;font-weight:700">crescendo…</small><?php endif; ?>
        </td>
        <td style="color:#64748b;font-size:.78rem"><?= htmlspecialchars($c['modo'] ?? '?') ?><?= !empty($c['comprimido']) ? ' + .tar.gz' : '' ?></td>
        <td style="color:#475569"><?= htmlspecialchars($c['host_cliente'] ?? '?') ?></td>
        <td style="color:#94a3b8;font-family:monospace;font-size:.75rem"><?= htmlspecialchars($c['ip_remoto'] ?? '?') ?></td>
      </tr>
      <tr id="d<?= $i ?>" class="detalhes" style="display:none">
        <td colspan="9">
          <div class="detalhe-grid">
            <div><small>Cliente</small><strong><?= htmlspecialchars($c['cliente'] ?? '?') ?></strong></div>
            <div><small>Job ID</small><strong style="font-family:monospace"><?= htmlspecialchars($c['job_id'] ?? '?') ?></strong></div>
            <div><small>Data/hora exata</small><strong><?= htmlspecialchars($c['em'] ?? '?') ?></strong></div>
            <div><small>Duração</small><strong><?= number_format((float)($c['duracao_seg'] ?? 0), 1, ',', '.') ?> segundos</strong></div>
            <div><small>Tamanho final</small><strong><?= fmtMB((int)($c['tamanho'] ?? 0)) ?></strong></div>
            <div><small>Modo backup</small><strong><?= htmlspecialchars($c['modo'] ?? '?') ?> <?= !empty($c['comprimido']) ? ' (com tar.gz)' : '(sem compressão)' ?></strong></div>
            <div><small>Versão BI</small><strong><?= htmlspecialchars($c['versao_bi'] ?? '?') ?></strong></div>
            <div><small>Recebido em</small><strong><?= htmlspecialchars($c['recebido_em'] ?? '?') ?></strong></div>
          </div>
          <?php if ($emAndamento): ?>
            <div style="background:#dbeafe;border:1px solid #60a5fa;border-radius:6px;padding:.8rem 1rem;margin-bottom:1rem">
              <div style="font-size:.95rem;font-weight:700;color:#1d4ed8;margin-bottom:.4rem">
                🔄 Backup rodando agora — <?= htmlspecialchars($c['estagio'] ?? '?') ?>
              </div>
              <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:.5rem;font-size:.82rem">
                <div><strong style="color:#1e40af">Iniciado em:</strong><br><?= htmlspecialchars($c['iniciado_em'] ?? '?') ?></div>
                <div><strong style="color:#1e40af">Tempo decorrido:</strong><br><?= floor((int)($c['tempo_decorrido'] ?? 0)/60) ?>min <?= (int)($c['tempo_decorrido'] ?? 0)%60 ?>s</div>
                <div><strong style="color:#1e40af">Tamanho atual:</strong><br><?= fmtMB((int)($c['tamanho_atual'] ?? 0)) ?></div>
                <div><strong style="color:#1e40af">Última atualização:</strong><br><?= ago($em) ?></div>
              </div>
              <?php if ($cls === 'travado'): ?>
                <div class="bk-travado-warn" style="margin-top:.7rem">
                  ⚠️ <strong>Sem heartbeat há <?= floor((time()-$em)/60) ?> minutos.</strong>
                  O processo pode ter travado. Confira no servidor do cliente se o gbak.exe (PID
                  <?= !empty($c['pid']) ? '<code>'.htmlspecialchars((string)$c['pid']).'</code>' : 'desconhecido' ?>)
                  ainda está vivo. Lock vale por 2h.
                  <?php if (!empty($c['_arquivo_json'])): ?>
                    <div style="margin-top:.6rem">
                      <a href="?limpar=<?= urlencode($c['_arquivo_json']) ?>"
                         onclick="event.stopPropagation(); return confirm('Apagar este registro travado?\n\nO backup no cliente pode ter falhado silenciosamente — o registro fica preso porque ninguém reportou fim. O cliente reaparece no próximo ping.');"
                         style="display:inline-block;background:#dc2626;color:#fff;padding:.35rem .7rem;border-radius:6px;text-decoration:none;font-size:.78rem;font-weight:700">
                        🗑 Limpar este registro travado
                      </a>
                    </div>
                  <?php endif; ?>
                </div>
              <?php endif; ?>
            </div>
          <?php endif; ?>

          <?php if (!empty($c['erro'])): ?>
            <div class="erro-bloco">
              ❌ <strong>Erro:</strong> <?= htmlspecialchars($c['erro']) ?>
              <?php if (!empty($c['tentativa_atual'])): ?>
                <div style="margin-top:.4rem;font-size:.8rem;color:#7f1d1d">
                  📊 Tentativa <strong><?= (int)$c['tentativa_atual'] ?> de <?= (int)($c['max_tentativas'] ?? 3) ?></strong>
                  <?php if (!empty($c['proxima_tentativa_em'])): ?>
                    — próxima automática em <strong><?= date('d/m H:i', strtotime($c['proxima_tentativa_em'])) ?></strong>
                  <?php else: ?>
                    — <strong>desistiu</strong> (próxima execução só no horário agendado)
                  <?php endif; ?>
                </div>
              <?php endif; ?>
            </div>
          <?php endif; ?>
          <?php if (!empty($c['log'])): ?>
            <details style="margin-top:.6rem">
              <summary style="cursor:pointer;color:#475569;font-size:.85rem;font-weight:700">📋 Log detalhado do backup (stderr do gbak)</summary>
              <pre style="background:#1f2937;color:#f3f4f6;padding:.8rem;border-radius:6px;font-size:.72rem;max-height:300px;overflow:auto"><?= htmlspecialchars((string)$c['log']) ?></pre>
            </details>
          <?php endif; ?>

          <?php /* ── Downloads do backup (v2.1 — 30/06/2026) ─────────────────────── */ ?>
          <?php
          $uploadsOk = array_filter((array)($c['uploads'] ?? []), fn($u) => !empty($u['ok']));
          if (!empty($uploadsOk)):
          ?>
            <div style="margin-top:.8rem;background:#ecfdf5;border:1px solid #6ee7b7;border-radius:8px;padding:.7rem 1rem">
              <div style="font-size:.85rem;font-weight:700;color:#047857;margin-bottom:.4rem">📦 Downloads disponíveis</div>
              <?php foreach ($uploadsOk as $up):
                $tipo = (string)($up['tipo'] ?? '');
                $idRemoto = (string)($up['id'] ?? '');
              ?>
                <?php if ($tipo === 'google_drive' && $idRemoto !== ''): ?>
                  <div style="display:flex;align-items:center;gap:.5rem;margin-bottom:.3rem">
                    <span style="font-size:1.2rem">📁</span>
                    <span style="flex:1;font-size:.85rem">
                      <strong>Google Drive</strong>
                      <span style="color:#64748b;font-size:.75rem">• ID: <?= htmlspecialchars(substr($idRemoto, 0, 20)) ?>…</span>
                    </span>
                    <a href="https://drive.google.com/uc?export=download&id=<?= urlencode($idRemoto) ?>"
                       target="_blank"
                       style="background:#2563eb;color:#fff;padding:.35rem .8rem;border-radius:6px;text-decoration:none;font-size:.8rem;font-weight:700">
                      ⬇️ Baixar
                    </a>
                    <a href="https://drive.google.com/file/d/<?= urlencode($idRemoto) ?>/view"
                       target="_blank"
                       style="background:#fff;border:1px solid #cbd5e1;color:#475569;padding:.35rem .7rem;border-radius:6px;text-decoration:none;font-size:.8rem">
                      🔍 Abrir no Drive
                    </a>
                  </div>
                <?php elseif ($tipo === 'ftp'): ?>
                  <div style="font-size:.82rem;color:#475569;margin-bottom:.3rem">
                    🌐 <strong>FTP</strong> — acesse manualmente no servidor FTP configurado
                  </div>
                <?php elseif ($tipo === 'pasta_local' || $tipo === 'rede'): ?>
                  <div style="font-size:.82rem;color:#475569;margin-bottom:.3rem">
                    💾 <strong><?= $tipo === 'rede' ? 'Pasta de rede' : 'Pasta local' ?></strong> — copie do servidor do cliente
                  </div>
                <?php endif; ?>
              <?php endforeach; ?>
              <div style="font-size:.7rem;color:#64748b;margin-top:.3rem;border-top:1px solid #d1fae5;padding-top:.35rem">
                💡 Pra "Baixar" funcionar, esteja logado no Google com a conta do backup
                (mesma do <code>config/backup-oauth.php</code> no BI).
              </div>
            </div>
          <?php endif; ?>

          <details>
            <summary style="cursor:pointer;color:#475569;font-size:.85rem;font-weight:700">🔬 JSON completo</summary>
            <pre><?= htmlspecialchars(json_encode($c, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></pre>
          </details>
        </td>
      </tr>
    <?php endforeach; ?>
  </tbody>
</table>

<p style="margin-top:1.5rem;color:#94a3b8;font-size:.78rem">💡 Cada arquivo em <code>storage/</code> é 1 job de cliente. Cliques na linha pra abrir/fechar detalhes.</p>
</body>
</html>
