<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
session_start();
require_once "db.php";

if(!isset($_SESSION['admin'])){
    die("Access denied");
}

$apiKey = "sk_live_uqSkHKwJDJjKgrQZR8knNJHHtsSOFK3dybnD";

/* ================= HELPER: extract tracking/nin from an API response and build a reply string ================= */
function buildAutoReply($response){
    if(!is_array($response)) return null;

    // API sometimes nests fields under "data", sometimes returns them top-level
    $d = $response['data'] ?? $response;

    $tracking = $d['tracking_id'] ?? $d['trackingId'] ?? $response['tracking_id'] ?? null;
    $nin      = $d['nin'] ?? $response['nin'] ?? null;

    $parts = [];
    if(!empty($tracking)) $parts[] = "Tracking ID: {$tracking}";
    if(!empty($nin))      $parts[] = "NIN: {$nin}";

    return $parts ? implode(" | ", $parts) : null;
}

/* ============================================================
   AJAX ENDPOINT — everything (check / retry / update) goes
   through here and returns JSON. No page reloads.
   ============================================================ */
if(isset($_POST['ajax_action'])){

    header('Content-Type: application/json');
    $action = $_POST['ajax_action'];
    $id     = (int)($_POST['id'] ?? 0);

    /* -------- CHECK STATUS -------- */
    if($action === 'check'){

        $get = $conn->prepare("SELECT transaction_id, reply FROM ipe_request WHERE id=?");
        $get->bind_param("i", $id);
        $get->execute();
        $row = $get->get_result()->fetch_assoc();

        if(!$row || !$row['transaction_id']){
            echo json_encode(["success"=>false, "message"=>"No transaction id on record"]);
            exit;
        }

        $ch = curl_init("https://new.unifyxpress.com/api/v4/transaction/".$row['transaction_id']);
        curl_setopt_array($ch,[
            CURLOPT_RETURNTRANSFER=>true,
            CURLOPT_TIMEOUT=>15,
            CURLOPT_HTTPHEADER=>["X-API-Key: $apiKey"]
        ]);
        $res = curl_exec($ch);
        $curlErr = curl_error($ch);
        curl_close($ch);

        if($res === false){
            echo json_encode(["success"=>false, "message"=>"API error: ".$curlErr]);
            exit;
        }

        $response = json_decode($res, true);
        $status   = strtolower($response['status'] ?? 'failed');
        $raw      = json_encode($response);
        $autoReply = buildAutoReply($response);
        $reply    = $autoReply ?: $row['reply'];

        if($autoReply){
            $stmt = $conn->prepare("UPDATE ipe_request SET request_status=?, raw_response=?, reply=? WHERE id=?");
            $stmt->bind_param("sssi", $status, $raw, $autoReply, $id);
        } else {
            $stmt = $conn->prepare("UPDATE ipe_request SET request_status=?, raw_response=? WHERE id=?");
            $stmt->bind_param("ssi", $status, $raw, $id);
        }
        $stmt->execute();

        echo json_encode([
            "success"=>true,
            "id"=>$id,
            "status"=>$status,
            "reply"=>$reply,
            "raw"=>$raw
        ]);
        exit;
    }

    /* -------- RETRY -------- */
    if($action === 'retry'){

        $get = $conn->prepare("SELECT tracking_id, reply FROM ipe_request WHERE id=?");
        $get->bind_param("i", $id);
        $get->execute();
        $row = $get->get_result()->fetch_assoc();

        if(!$row || !$row['tracking_id']){
            echo json_encode(["success"=>false, "message"=>"No tracking id on record"]);
            exit;
        }

        $payload = json_encode(["tracking_ids"=>[$row['tracking_id']]]);

        $ch = curl_init("https://new.unifyxpress.com/api/v4/nin/ipe");
        curl_setopt_array($ch,[
            CURLOPT_RETURNTRANSFER=>true,
            CURLOPT_TIMEOUT=>15,
            CURLOPT_POST=>true,
            CURLOPT_POSTFIELDS=>$payload,
            CURLOPT_HTTPHEADER=>[
                "Content-Type: application/json",
                "X-API-Key: $apiKey"
            ]
        ]);
        $res = curl_exec($ch);
        $curlErr = curl_error($ch);
        curl_close($ch);

        if($res === false){
            echo json_encode(["success"=>false, "message"=>"API error: ".$curlErr]);
            exit;
        }

        $data = json_decode($res, true);

        if(!isset($data['transactions'][0])){
            echo json_encode(["success"=>false, "message"=>"No transaction returned"]);
            exit;
        }

        $t = $data['transactions'][0];
        $raw = json_encode($t);
        $status = strtolower($t['status'] ?? 'pending');
        $autoReply = buildAutoReply($t);
        $reply = $autoReply ?: $row['reply'];

        if($autoReply){
            $stmt = $conn->prepare("UPDATE ipe_request SET transaction_id=?, request_status=?, raw_response=?, reply=? WHERE id=?");
            $stmt->bind_param("ssssi", $t['id'], $status, $raw, $autoReply, $id);
        } else {
            $stmt = $conn->prepare("UPDATE ipe_request SET transaction_id=?, request_status=?, raw_response=? WHERE id=?");
            $stmt->bind_param("sssi", $t['id'], $status, $raw, $id);
        }
        $stmt->execute();

        echo json_encode([
            "success"=>true,
            "id"=>$id,
            "status"=>$status,
            "reply"=>$reply,
            "raw"=>$raw
        ]);
        exit;
    }

    /* -------- MANUAL UPDATE (admin sets status/reply) -------- */
    if($action === 'update'){

        $status = $_POST['status'] ?? '';
        $reply  = $_POST['reply'] ?? '';

        $get = $conn->prepare("SELECT * FROM ipe_request WHERE id=?");
        $get->bind_param("i", $id);
        $get->execute();
        $row = $get->get_result()->fetch_assoc();

        if(!$row){
            echo json_encode(["success"=>false, "message"=>"Record not found"]);
            exit;
        }

        // refund logic (UNCHANGED)
        if($status == "failed" && $row['request_status'] != "failed"){
            $stmt = $conn->prepare("UPDATE balance SET amount = amount + ? WHERE user=?");
            $stmt->bind_param("ds", $row['amount'], $row['user']);
            $stmt->execute();
        }

        $stmt = $conn->prepare("UPDATE ipe_request SET request_status=?, reply=? WHERE id=?");
        $stmt->bind_param("ssi", $status, $reply, $id);
        $stmt->execute();

        echo json_encode([
            "success"=>true,
            "id"=>$id,
            "status"=>$status,
            "reply"=>$reply,
            "raw"=>$row['raw_response']
        ]);
        exit;
    }

    echo json_encode(["success"=>false, "message"=>"Unknown action"]);
    exit;
}

/* ================= TOP STATS ================= */
$total   = $conn->query("SELECT COUNT(*) t FROM ipe_request")->fetch_assoc()['t'];
$success = $conn->query("SELECT COUNT(*) t FROM ipe_request WHERE request_status='successful'")->fetch_assoc()['t'];
$failed  = $conn->query("SELECT COUNT(*) t FROM ipe_request WHERE request_status='failed'")->fetch_assoc()['t'];

/* ================= ORIGINAL STATS ================= */
$daily   = $conn->query("SELECT COUNT(*) c, SUM(amount) s FROM ipe_request WHERE DATE(created_at)=CURDATE()")->fetch_assoc();
$weekly  = $conn->query("SELECT COUNT(*) c, SUM(amount) s FROM ipe_request WHERE YEARWEEK(created_at)=YEARWEEK(NOW())")->fetch_assoc();
$monthly = $conn->query("SELECT COUNT(*) c, SUM(amount) s FROM ipe_request WHERE MONTH(created_at)=MONTH(NOW())")->fetch_assoc();

/* ================= PAGINATION ================= */
$perPage    = 10;
$page       = isset($_GET['page']) ? (int)$_GET['page'] : 1;
if($page < 1) $page = 1;
$totalRows  = (int)$conn->query("SELECT COUNT(*) t FROM ipe_request")->fetch_assoc()['t'];
$totalPages = max(1, (int)ceil($totalRows / $perPage));
if($page > $totalPages) $page = $totalPages;
$offset     = ($page - 1) * $perPage;

/* ================= FETCH ================= */
$data = $conn->query("SELECT * FROM ipe_request ORDER BY id DESC LIMIT $perPage OFFSET $offset");
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>IPE Admin Dashboard</title>

<style>
:root{
  --brand1:#6a5cff;
  --brand2:#00c9c8;
  --bg:#f2f4fb;
  --card-bg:#ffffff;
  --text:#1f2436;
  --muted:#8a90a6;
  --pending:#ffb020;
  --processing:#3b82f6;
  --successful:#22c55e;
  --failed:#ef4444;
  --radius:16px;
  --shadow:0 8px 24px rgba(30,34,90,.08);
}
*{box-sizing:border-box}
body{
  font-family:'Segoe UI',Roboto,Arial,sans-serif;
  background:var(--bg);
  margin:0;
  color:var(--text);
}
.header{
  padding:22px 20px;
  font-size:22px;
  font-weight:800;
  color:#fff;
  background:linear-gradient(135deg,var(--brand1),var(--brand2));
  display:flex;
  align-items:center;
  gap:10px;
  box-shadow:var(--shadow);
  position:sticky;
  top:0;
  z-index:20;
}
.header .dot{
  width:10px;height:10px;border-radius:50%;
  background:#00ffb2;
  box-shadow:0 0 0 0 rgba(0,255,178,.6);
  animation:pulse 2s infinite;
}
@keyframes pulse{
  0%{box-shadow:0 0 0 0 rgba(0,255,178,.6)}
  70%{box-shadow:0 0 0 10px rgba(0,255,178,0)}
  100%{box-shadow:0 0 0 0 rgba(0,255,178,0)}
}

.cards{display:flex;gap:14px;padding:16px;flex-wrap:wrap}
.card{
  flex:1;min-width:170px;
  background:var(--card-bg);
  padding:16px 18px;
  border-radius:var(--radius);
  box-shadow:var(--shadow);
  border-top:4px solid var(--brand1);
  transition:transform .15s ease;
}
.card:hover{transform:translateY(-3px)}
.card b{display:block;font-size:13px;color:var(--muted);margin-bottom:6px;letter-spacing:.5px;text-transform:uppercase}
.card .big{font-size:24px;font-weight:800}
.card.grad{
  background:linear-gradient(135deg,var(--brand1),var(--brand2));
  color:#fff;border-top:none;
}
.card.grad b{color:rgba(255,255,255,.85)}

.table-wrap{background:var(--card-bg);margin:16px;border-radius:var(--radius);box-shadow:var(--shadow);overflow:hidden}
table{width:100%;border-collapse:collapse}
th{
  background:linear-gradient(135deg,var(--brand1),var(--brand2));
  color:#fff;padding:14px 10px;font-size:13px;text-align:left;
}
td{padding:12px 10px;border-bottom:1px solid #eef0f6;font-size:13px;vertical-align:top}
tr:hover td{background:#f7f8ff}

.badge{padding:6px 12px;border-radius:20px;color:#fff;font-size:11px;font-weight:700;letter-spacing:.4px;display:inline-block;text-transform:capitalize}
.badge.pending{background:var(--pending)}
.badge.processing{background:var(--processing)}
.badge.successful{background:var(--successful)}
.badge.failed{background:var(--failed)}

button{padding:7px 10px;border:none;border-radius:8px;cursor:pointer;font-size:12px;font-weight:600;transition:opacity .15s}
button:disabled{opacity:.5;cursor:not-allowed}
button:hover:not(:disabled){opacity:.85}
.btn-check{background:var(--pending);color:#fff;margin-right:6px}
.btn-retry{background:var(--processing);color:#fff}
.btn-update{background:var(--brand1);color:#fff;margin-top:6px;width:100%}
.action-group{display:flex;flex-direction:column;gap:6px}

select,textarea{width:100%;padding:7px;border-radius:8px;border:1px solid #dde0ec;font-size:12px;font-family:inherit}
textarea{min-height:44px;resize:vertical}

.spinner{
  width:12px;height:12px;border:2px solid rgba(255,255,255,.5);
  border-top-color:#fff;border-radius:50%;
  display:inline-block;animation:spin .6s linear infinite;margin-right:5px;vertical-align:-2px;
}
@keyframes spin{to{transform:rotate(360deg)}}

/* POPUP */
.popup{display:none;position:fixed;inset:0;background:rgba(15,17,30,.7);align-items:center;justify-content:center;z-index:100}
.popup-box{background:#fff;width:90%;max-width:420px;border-radius:14px;overflow:hidden;box-shadow:0 20px 60px rgba(0,0,0,.3)}
.popup-header{background:linear-gradient(135deg,var(--brand1),var(--brand2));color:#fff;padding:14px 16px;display:flex;justify-content:space-between;align-items:center;font-weight:700}
.popup-header span:last-child{cursor:pointer;font-size:16px}
.popup-body{padding:18px;color:#000;font-size:14px;line-height:1.6}

/* TOAST */
#toast{
  position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(20px);
  background:#1f2436;color:#fff;padding:12px 20px;border-radius:10px;
  font-size:13px;opacity:0;pointer-events:none;transition:all .25s ease;z-index:200;
  box-shadow:0 10px 30px rgba(0,0,0,.25);
}
#toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
#toast.error{background:var(--failed)}
#toast.ok{background:var(--successful)}

/* PAGINATION */
.pagination{
  display:flex;flex-wrap:wrap;align-items:center;gap:6px;
  padding:16px;border-top:1px solid #eef0f6;
}
.page-btn{
  padding:8px 12px;border-radius:8px;background:#eef0fb;color:var(--text);
  text-decoration:none;font-size:13px;font-weight:600;transition:all .15s;
}
.page-btn:hover{background:#e1e4fb}
.page-btn.active{background:linear-gradient(135deg,var(--brand1),var(--brand2));color:#fff}
.page-btn.disabled{opacity:.4;pointer-events:none}
.page-dots{padding:0 4px;color:var(--muted)}
.page-info{margin-left:auto;font-size:12px;color:var(--muted)}
@media (max-width:760px){
  .page-info{width:100%;margin-left:0;order:99;text-align:center;margin-top:6px}
}

/* MOBILE RESPONSIVE TABLE */
@media (max-width: 760px){
  th{position:static}
  .table-wrap{margin:10px}
  table, thead, tbody, th, td, tr{display:block;width:100%}
  thead tr{position:absolute;top:-9999px;left:-9999px}
  tr{margin-bottom:14px;border-radius:12px;box-shadow:var(--shadow);overflow:hidden}
  td{
    border:none;border-bottom:1px solid #f0f1f6;
    display:flex;justify-content:space-between;align-items:flex-start;gap:10px;
    text-align:right;
  }
  td::before{
    content:attr(data-label);
    font-weight:700;color:var(--muted);text-align:left;
    text-transform:uppercase;font-size:11px;flex-shrink:0;
  }
  .cards{padding:10px}
  .card{min-width:100%}
}
</style>
</head>

<body>

<div class="header"><span class="dot"></span> IPE Admin Dashboard</div>

<div class="cards">
<div class="card grad"><b>Total</b><span class="big"><?= $total ?></span></div>
<div class="card grad"><b>Successful</b><span class="big"><?= $success ?></span></div>
<div class="card grad"><b>Failed</b><span class="big"><?= $failed ?></span></div>
</div>

<div class="cards">
<div class="card">
<b>Transactions</b>
Daily: <?= $daily['c'] ?? 0 ?><br>
Weekly: <?= $weekly['c'] ?? 0 ?><br>
Monthly: <?= $monthly['c'] ?? 0 ?>
</div>

<div class="card">
<b>Amount</b>
Daily: ₦<?= number_format($daily['s'] ?? 0,2) ?><br>
Weekly: ₦<?= number_format($weekly['s'] ?? 0,2) ?><br>
Monthly: ₦<?= number_format($monthly['s'] ?? 0,2) ?>
</div>

<div class="card">
<b>Profit</b>
Daily: ₦<?= number_format(($daily['s'] ?? 0)*0.2,2) ?><br>
Weekly: ₦<?= number_format(($weekly['s'] ?? 0)*0.2,2) ?><br>
Monthly: ₦<?= number_format(($monthly['s'] ?? 0)*0.2,2) ?>
</div>
</div>

<div class="table-wrap">
<table>
<thead>
<tr>
<th>User</th>
<th>Tracking</th>
<th>Price</th>
<th>Status</th>
<th>Report</th>
<th>Actions</th>
<th>Update</th>
</tr>
</thead>
<tbody id="rows">

<?php while($row = $data->fetch_assoc()): ?>
<tr data-id="<?= $row['id'] ?>" data-status="<?= htmlspecialchars($row['request_status']) ?>">

<td data-label="User"><?= htmlspecialchars($row['user_email'] ?? '') ?></td>
<td data-label="Tracking"><?= htmlspecialchars($row['tracking_id'] ?? '') ?></td>
<td data-label="Price">₦<?= number_format($row['amount'],2) ?></td>

<td data-label="Status">
<span class="badge <?= htmlspecialchars($row['request_status']) ?>" data-badge>
<?= ucfirst($row['request_status']) ?>
</span>
</td>

<td data-label="Report">
<?php if($row['raw_response'] || $row['reply']): ?>
<button class="btn-view" data-raw="<?= htmlspecialchars($row['raw_response'] ?: '', ENT_QUOTES) ?>" onclick="viewReport(this.dataset.raw)">View</button>
<?php else: ?>
<span data-noreport>--</span>
<?php endif; ?>
</td>

<td data-label="Actions">
<div class="action-group">
<button type="button" class="btn-check" data-id="<?= $row['id'] ?>">Check</button>
<button type="button" class="btn-retry" data-id="<?= $row['id'] ?>">Retry</button>
</div>
</td>

<td data-label="Update">
<form class="form-update" data-id="<?= $row['id'] ?>">
<select name="status">
<option value="pending"   <?= $row['request_status']=='pending'?'selected':'' ?>>Pending</option>
<option value="processing"<?= $row['request_status']=='processing'?'selected':'' ?>>Processing</option>
<option value="successful"<?= $row['request_status']=='successful'?'selected':'' ?>>Successful</option>
<option value="failed"    <?= $row['request_status']=='failed'?'selected':'' ?>>Failed</option>
</select>
<textarea name="reply" placeholder="Enter reply" data-reply><?= htmlspecialchars($row['reply'] ?? '') ?></textarea>
<button type="submit" class="btn-update">Save</button>
</form>
</td>

</tr>
<?php endwhile; ?>

</tbody>
</table>

<?php if($totalPages > 1): ?>
<div class="pagination">
<?php
$qs = $_GET;
function pageUrl($p, $qs){ $qs['page'] = $p; return '?' . http_build_query($qs); }
?>

<a class="page-btn <?= $page<=1 ? 'disabled' : '' ?>" href="<?= $page>1 ? pageUrl($page-1,$qs) : '#' ?>">&laquo; Prev</a>

<?php
$window = 2;
for($p = 1; $p <= $totalPages; $p++){
    if($p == 1 || $p == $totalPages || ($p >= $page-$window && $p <= $page+$window)){
        echo '<a class="page-btn '.($p==$page?'active':'').'" href="'.pageUrl($p,$qs).'">'.$p.'</a>';
    } elseif($p == $page-$window-1 || $p == $page+$window+1){
        echo '<span class="page-dots">…</span>';
    }
}
?>

<a class="page-btn <?= $page>=$totalPages ? 'disabled' : '' ?>" href="<?= $page<$totalPages ? pageUrl($page+1,$qs) : '#' ?>">Next &raquo;</a>

<span class="page-info">Page <?= $page ?> of <?= $totalPages ?> (<?= $totalRows ?> requests)</span>
</div>
<?php endif; ?>

</div>

<!-- POPUP -->
<div id="popup" class="popup">
<div class="popup-box">
<div class="popup-header">
<span>Report</span>
<span onclick="closePop()">✖</span>
</div>
<div class="popup-body" id="content"></div>
</div>
</div>

<div id="toast"></div>

<script>
const AJAX_URL = window.location.href;

/* ---------- Toast ---------- */
let toastTimer;
function toast(msg, type){
  const el = document.getElementById('toast');
  el.textContent = msg;
  el.className = 'show' + (type ? ' ' + type : '');
  clearTimeout(toastTimer);
  toastTimer = setTimeout(()=>{ el.className = ''; }, 2500);
}

/* ---------- Report popup ---------- */
function viewReport(data){
  let html = "";
  try{
    const d = JSON.parse(data);
    html += "<b>Status:</b> " + (d.status || 'N/A') + "<br><br>";
    html += "<b>Tracking:</b> " + (d.data?.tracking_id || d.tracking_id || 'N/A') + "<br><br>";
    html += "<b>NIN:</b> " + (d.data?.nin || d.nin || 'N/A');
  }catch(e){
    html = data || 'No data';
  }
  document.getElementById("content").innerHTML = html;
  document.getElementById("popup").style.display = "flex";
}
function closePop(){
  document.getElementById("popup").style.display = "none";
}

/* ---------- Core AJAX call ---------- */
async function callAjax(action, id, extra){
  const fd = new FormData();
  fd.append('ajax_action', action);
  fd.append('id', id);
  if(extra){
    for(const k in extra) fd.append(k, extra[k]);
  }
  const res = await fetch(AJAX_URL, { method:'POST', body: fd });
  return res.json();
}

/* ---------- Update a row's UI from a JSON result ---------- */
function updateRowUI(json){
  const tr = document.querySelector(`tr[data-id="${json.id}"]`);
  if(!tr) return;

  tr.dataset.status = json.status;

  const badge = tr.querySelector('[data-badge]');
  if(badge){
    badge.className = 'badge ' + json.status;
    badge.textContent = json.status.charAt(0).toUpperCase() + json.status.slice(1);
  }

  if(json.reply !== undefined && json.reply !== null){
    const replyBox = tr.querySelector('[data-reply]');
    if(replyBox) replyBox.value = json.reply;
  }

  if(json.raw){
    let viewBtn = tr.querySelector('.btn-view');
    const reportCell = tr.querySelector('[data-noreport]')?.parentElement;
    if(viewBtn){
      viewBtn.dataset.raw = json.raw;
    } else if(reportCell){
      reportCell.innerHTML = `<button class="btn-view" data-raw='${json.raw.replace(/'/g,"&#39;")}' onclick="viewReport(this.dataset.raw)">View</button>`;
    }
  }
}

/* ---------- Check ---------- */
async function doCheck(id, btn){
  if(btn) { btn.disabled = true; btn.innerHTML = '<span class="spinner"></span>Checking'; }
  try{
    const json = await callAjax('check', id);
    if(json.success){
      updateRowUI(json);
    } else if(btn){
      toast(json.message || 'Check failed', 'error');
    }
  }catch(err){
    if(btn) toast('Network error while checking', 'error');
  }
  if(btn) { btn.disabled = false; btn.textContent = 'Check'; }
}

/* ---------- Retry ---------- */
async function doRetry(id, btn){
  if(!confirm('Retry request?')) return;
  btn.disabled = true; btn.innerHTML = '<span class="spinner"></span>Retrying';
  try{
    const json = await callAjax('retry', id);
    if(json.success){
      updateRowUI(json);
      toast('Retry submitted', 'ok');
    } else {
      toast(json.message || 'Retry failed', 'error');
    }
  }catch(err){
    toast('Network error while retrying', 'error');
  }
  btn.disabled = false; btn.textContent = 'Retry';
}

/* ---------- Manual update ---------- */
async function doUpdate(form){
  const id = form.dataset.id;
  const status = form.querySelector('[name="status"]').value;
  const reply = form.querySelector('[name="reply"]').value;
  const btn = form.querySelector('.btn-update');
  btn.disabled = true; btn.innerHTML = '<span class="spinner"></span>Saving';
  try{
    const json = await callAjax('update', id, { status, reply });
    if(json.success){
      updateRowUI(json);
      toast('Updated successfully', 'ok');
    } else {
      toast(json.message || 'Update failed', 'error');
    }
  }catch(err){
    toast('Network error while saving', 'error');
  }
  btn.disabled = false; btn.textContent = 'Save';
}

/* ---------- Wire up events ---------- */
function attachHandlers(){
  document.querySelectorAll('.btn-check').forEach(btn=>{
    btn.addEventListener('click', ()=> doCheck(btn.dataset.id, btn));
  });
  document.querySelectorAll('.btn-retry').forEach(btn=>{
    btn.addEventListener('click', ()=> doRetry(btn.dataset.id, btn));
  });
  document.querySelectorAll('.form-update').forEach(form=>{
    form.addEventListener('submit', e=>{ e.preventDefault(); doUpdate(form); });
  });
}

/* ---------- Auto-poll every 5s for pending/processing rows ---------- */
function startPolling(){
  setInterval(()=>{
    document.querySelectorAll('tr[data-status="pending"], tr[data-status="processing"]').forEach(tr=>{
      doCheck(tr.dataset.id, null); // silent, no button spinner
    });
  }, 5000);
}

document.addEventListener('DOMContentLoaded', ()=>{
  attachHandlers();
  startPolling();
});
</script>

</body>
</html>
