<?php
require_once __DIR__ . '/includes/auth.php';
require_once __DIR__ . '/includes/channel.php';
chat247_start_session();

// Capture an affiliate / CB tag if this page was the entry point of a CB hop
// link (?aff=<nick> or ?cb=1). Writes the 30-day cb_channel cookie before any
// output so a later signup POST carries it. Idempotent for organic visitors.
chat247_resolve_channel();

// Allow OAuth callback's #bearer=... fragment to bootstrap the session via
// a tiny inline JS shim (fragments aren't sent server-side).

// Pop staged plan from Google-OAuth signup wizard. The plan picker on
// signup.php tucks it into the session before bouncing to Google; we read it
// here once and clear it. JS below uses this to auto-fire Stripe Checkout.
$staged_plan = '';
$ALLOWED_PLANS = ['free' => 1, 'monthly' => 1, 'annual' => 1];
if (!empty($_SESSION['signup_plan']) && isset($ALLOWED_PLANS[$_SESSION['signup_plan']])) {
    $staged_plan = $_SESSION['signup_plan'];
    unset($_SESSION['signup_plan']);
}
?><!doctype html>
<html lang="en"><head>
<base href="https://<?= htmlspecialchars($_SERVER['HTTP_HOST'] ?? '', ENT_QUOTES) ?>/"><meta charset="utf-8"><title>Dashboard — 247ch.at</title>
<link rel="icon" href="/favicon.ico?v=<?= @filemtime(__DIR__ . '/favicon.ico') ?: time() ?>">
<style>
 :root { --bg:#0b0f1a; --panel:#111827; --fg:#f3f4f6; --muted:#9ca3af; --brand:#3b82f6; --border:#1f2937; }
 * { box-sizing:border-box; }
 body { margin:0; font-family:-apple-system,sans-serif; background:var(--bg); color:var(--fg); }
 header { display:flex; align-items:center; justify-content:space-between; padding:14px 24px; border-bottom:1px solid var(--border); }
 header .logo { font-weight:700; }
 header a { color:var(--fg); text-decoration:none; margin-left:18px; }
 header .header-search { display:flex; margin:0 12px; flex:0 0 auto; }
 header .header-search input { background:var(--panel); border:1px solid var(--border); color:var(--fg); padding:7px 12px; border-radius:6px; font-size:13px; width:220px; outline:none; }
 header .header-search input:focus { border-color:var(--brand); }
 @media (max-width:640px) { header .header-search { display:none; } }
 main { padding:32px; max-width:960px; margin:0 auto; }
 h1 { margin:0 0 24px; font-size:22px; }
 .card { background:var(--panel); border-radius:12px; padding:24px; margin-bottom:24px; }
 .card h2 { margin:0 0 16px; font-size:16px; }
 table { width:100%; border-collapse:collapse; }
 th,td { padding:10px; border-bottom:1px solid var(--border); text-align:left; font-size:14px; }
 th { color:var(--muted); font-weight:500; }
 .swatch { display:inline-block; width:14px; height:14px; border-radius:3px; vertical-align:middle; margin-right:8px; }
 button.add { background:var(--brand); color:#fff; border:none; padding:10px 18px; border-radius:6px; cursor:pointer; }
 .empty { color:var(--muted); padding:18px; text-align:center; }
 .modal-bg { display:none; position:fixed; inset:0; background:rgba(0,0,0,0.6); align-items:center; justify-content:center; }
 .modal-bg.open { display:flex; }
 .modal { background:var(--panel); padding:24px; border-radius:12px; width:420px; }
 .modal label { display:block; font-size:13px; color:var(--muted); margin-top:12px; }
 .modal input,.modal select { width:100%; padding:10px; background:var(--bg); border:1px solid var(--border); color:var(--fg); border-radius:6px; }
 .modal .row { display:flex; gap:8px; margin-top:18px; }
 .modal button { flex:1; padding:10px; border-radius:6px; border:none; cursor:pointer; }
 .modal .save { background:var(--brand); color:#fff; }
 .modal .cancel { background:transparent; color:var(--fg); border:1px solid var(--border); }
 code.snippet { display:block; background:var(--bg); padding:12px; border-radius:6px; font-size:12px; color:#9cdcfe; overflow-x:auto; }
 .site-actions a { color:var(--brand); text-decoration:none; font-size:13px; margin-right:12px; }
 .site-actions a:hover { text-decoration:underline; }
 .snip-toggle { background:transparent; border:1px solid var(--border); color:var(--muted); padding:4px 10px; border-radius:5px; cursor:pointer; font-size:12px; }
 .snip-toggle:hover { color:var(--fg); border-color:var(--brand); }
 .snip-row { display:none; }
 .snip-row.open { display:table-row; }
 .snip-row td { background:var(--bg); padding:14px 16px; border-top:none; }
 .snip-row pre { margin:0; font-size:12px; color:#9cdcfe; white-space:pre-wrap; word-break:break-all; line-height:1.55; }
 .snip-copy { background:var(--brand); color:#fff; border:none; padding:5px 10px; border-radius:4px; cursor:pointer; font-size:11px; margin-left:8px; vertical-align:middle; }
 .billing-banner { display:none; align-items:center; justify-content:space-between; gap:16px; padding:20px 24px; border-radius:12px; margin-bottom:24px; }
 .billing-banner.show { display:flex; }
 .billing-banner.free { background:linear-gradient(90deg, #1d2a4a 0%, #1a2540 100%); border:1px solid #2e3f63; }
 .billing-banner.pro  { background:#0f3320; border:1px solid #1f7a3a; }
 .billing-banner h3 { margin:0 0 4px; font-size:16px; }
 .billing-banner p  { margin:0; color:var(--muted); font-size:13px; }
 .billing-banner.free h3 { color:#7ab1ff; }
 .billing-banner.pro  h3 { color:#56d68b; }
 .billing-banner button { background:var(--brand); color:#fff; border:none; padding:10px 18px; border-radius:6px; cursor:pointer; text-decoration:none; font-weight:600; font-size:14px; }
 .billing-banner.pro button { background:transparent; border:1px solid #56d68b; color:#56d68b; }
 .toast { position:fixed; bottom:24px; right:24px; background:var(--panel); border:1px solid var(--border); padding:14px 20px; border-radius:8px; max-width:360px; box-shadow:0 8px 24px rgba(0,0,0,0.5); display:none; }
 .toast.show { display:block; }
 .toast.err { border-color:#dc2626; color:#fecaca; }
 .usage-strip { display:none; background:var(--panel); border:1px solid var(--border); border-radius:12px; padding:16px 20px; margin-bottom:24px; }
 .usage-strip.show { display:block; }
 .usage-strip.warn { border-color:#d97706; }
 .usage-strip.over { border-color:#dc2626; }
 .usage-row { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom:10px; }
 .usage-row .label { font-size:14px; color:var(--muted); }
 .usage-row .count { font-size:14px; font-weight:600; color:var(--fg); }
 .usage-strip.warn .count { color:#fbbf24; }
 .usage-strip.over .count { color:#fca5a5; }
 .usage-bar { width:100%; height:8px; background:var(--bg); border-radius:999px; overflow:hidden; }
 .usage-bar .fill { height:100%; background:var(--brand); transition:width 0.3s; }
 .usage-strip.warn .usage-bar .fill { background:#d97706; }
 .usage-strip.over .usage-bar .fill { background:#dc2626; }
 .usage-cta { display:none; margin-top:12px; align-items:center; justify-content:space-between; gap:12px; padding-top:12px; border-top:1px solid var(--border); }
 .usage-strip.warn .usage-cta, .usage-strip.over .usage-cta { display:flex; }
 .usage-cta .msg { font-size:13px; color:var(--muted); flex:1; }
 .usage-strip.over .usage-cta .msg { color:#fca5a5; }
 .usage-cta button { background:var(--brand); color:#fff; border:none; padding:8px 14px; border-radius:6px; cursor:pointer; font-weight:600; font-size:13px; white-space:nowrap; }
</style></head><body>
<header>
  <div class="logo"><a href="/" style="text-decoration:none;color:var(--fg);">247ch.at</a></div>
  <form class="header-search" action="https://help.247ch.at/search.php" method="get" role="search">
    <input type="text" name="q" placeholder="Search docs…" aria-label="Search help docs">
  </form>
  <nav>
    <a href="/pricing">Pricing</a>
    <span id="userEmail" style="color:var(--muted); margin-left:18px;"></span>
    <a href="#" id="signoutBtn">Sign out</a>
  </nav>
</header>
<main>
  <h1>Your sites</h1>
  <div id="billingBanner" class="billing-banner">
    <div>
      <h3 id="billingTitle">Go Pro — $19/month · auto-renews</h3>
      <p id="billingDesc">$19/month · auto-renews · cancel anytime · 2500 AI replies/month included. Unlimited sites and no "Powered by 247ch.at" badge.</p>
    </div>
    <button id="billingBtn" onclick="onBillingClick()">Subscribe — $19/month</button>
  </div>
  <div id="usageStrip" class="usage-strip">
    <div class="usage-row">
      <span class="label">AI replies this month</span>
      <span class="count"><span id="usageUsed">0</span> / <span id="usageCap">0</span></span>
    </div>
    <div class="usage-bar"><div id="usageFill" class="fill" style="width:0%"></div></div>
    <div class="usage-cta">
      <span class="msg" id="usageMsg">Running low — top up to keep AI replies flowing.</span>
      <button id="usageTopupBtn" onclick="onTopupClick()">Add 1000 more for $5</button>
    </div>
  </div>
  <div class="card">
    <div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:16px;">
      <h2 style="margin:0;">Sites</h2>
      <button class="add" onclick="document.getElementById('addModal').classList.add('open')">+ Add site</button>
    </div>
    <table>
      <thead><tr><th>Domain</th><th>Color</th><th>Enabled</th><th>Install</th><th>Actions</th></tr></thead>
      <tbody id="sitesBody"><tr><td colspan="5" class="empty">Loading…</td></tr></tbody>
    </table>
  </div>
</main>

<div id="addModal" class="modal-bg">
  <div class="modal">
    <h2 style="margin:0 0 12px;">Add a site</h2>
    <label>Domain</label>
    <input id="newDomain" placeholder="example.com">
    <label>Display name</label>
    <input id="newName" placeholder="Example Corp">
    <label>Primary color</label>
    <input id="newColor" type="color" value="#3b82f6">
    <label>Position</label>
    <select id="newPosition">
      <option value="bottom-right">Bottom right</option>
      <option value="bottom-left">Bottom left</option>
    </select>
    <label>Greeting text</label>
    <input id="newGreeting" placeholder="How can we help?">
    <div class="row">
      <button class="cancel" onclick="document.getElementById('addModal').classList.remove('open')">Cancel</button>
      <button class="save" onclick="createSite()">Save</button>
    </div>
  </div>
</div>

<script>
// Read bearer from PHP session OR from OAuth callback's URL fragment.
(function() {
  let bearer = <?= json_encode($_SESSION['bearer'] ?? '') ?>;
  if (location.hash.startsWith('#bearer=')) {
    bearer = location.hash.slice(8);
    history.replaceState(null, '', location.pathname);
    fetch('/oauth/google_callback.php?bearer=' + encodeURIComponent(bearer), { credentials:'same-origin' });
  }
  if (!bearer) { location.href = '/login.php'; return; }
  window.__bearer = bearer;
})();

const RELAY = location.origin;

async function api(method, path, body) {
  const r = await fetch(RELAY + path, {
    method,
    headers: { 'Content-Type':'application/json', 'Authorization':'Bearer ' + window.__bearer },
    body: body ? JSON.stringify(body) : undefined,
  });
  if (r.status === 401) { location.href = '/login.php'; return; }
  return r.json();
}

let _mePlan = 'free';
async function loadMe() {
  const me = await api('GET', '/api/me');
  if (!me) return;
  if (me.email) document.getElementById('userEmail').textContent = me.email;
  _mePlan = me.plan || 'free';
  const banner = document.getElementById('billingBanner');
  const title  = document.getElementById('billingTitle');
  const desc   = document.getElementById('billingDesc');
  const btn    = document.getElementById('billingBtn');
  if (_mePlan === 'pro' && (me.subscriptionStatus === 'active' || me.subscriptionStatus === 'trialing')) {
    banner.className = 'billing-banner pro show';
    title.textContent = '247ch.at Pro · active';
    let renewsLine = '';
    if (me.subscriptionRenewsAt) {
      const d = new Date(me.subscriptionRenewsAt);
      renewsLine = ' Renews ' + d.toLocaleDateString(undefined, { month:'short', day:'numeric', year:'numeric' }) + '.';
    }
    desc.textContent = 'Unlimited sites + always-on AI + no powered-by badge.' + renewsLine;
    btn.textContent = 'Manage billing';
  } else {
    banner.className = 'billing-banner free show';
    title.textContent = 'Go Pro — $19/month · auto-renews';
    desc.textContent = '$19/month · auto-renews · cancel anytime · 2500 AI replies/month included. Unlimited sites and no "Powered by 247ch.at" badge.';
    btn.textContent = 'Subscribe — $19/month';
  }
}

async function startCheckout(plan) {
  // plan: 'monthly' | 'annual' — POSTs to /api/billing/checkout and follows
  // the Stripe redirect. Used by both the banner button (defaults monthly)
  // and the signup-wizard autostart (?subscribe=<plan>&autostart=1).
  const resp = await api('POST', '/api/billing/checkout', { plan: plan || 'monthly' });
  if (resp && resp.url) { location.href = resp.url; return true; }
  showToast('Could not start checkout' + (resp && resp.error ? ': ' + resp.error : ''), true);
  return false;
}

async function onBillingClick() {
  const btn = document.getElementById('billingBtn');
  const originalText = btn.textContent;
  btn.disabled = true;
  btn.textContent = '…';
  try {
    if (_mePlan === 'pro') {
      const resp = await api('POST', '/api/billing/portal', {});
      if (resp && resp.url) { location.href = resp.url; return; }
      showToast('Could not open billing portal' + (resp && resp.error ? ': ' + resp.error : ''), true);
      return;
    }
    // Free user clicking Subscribe -> default monthly. (Annual upsell lives on the wizard.)
    await startCheckout('monthly');
  } catch (err) {
    showToast('Network error: ' + err.message, true);
  } finally {
    btn.disabled = false;
    btn.textContent = originalText;
  }
}

function showToast(msg, isErr) {
  let t = document.getElementById('cbeToast');
  if (!t) {
    t = document.createElement('div');
    t.id = 'cbeToast';
    t.className = 'toast';
    document.body.appendChild(t);
  }
  t.textContent = msg;
  t.className = 'toast show' + (isErr ? ' err' : '');
  setTimeout(() => { t.className = 'toast'; }, 5000);
}

// Flash messages from Stripe Checkout return URLs.
(function() {
  const p = new URLSearchParams(location.search);
  if (p.get('subscribed') === '1') showToast('Subscription active. Thanks for going Pro!');
  else if (p.get('subscribed') === '0') showToast('Checkout canceled. You can subscribe anytime.', true);
  if (p.get('topup') === 'ok')      showToast('Top-up complete — +1000 AI replies added.');
  else if (p.get('topup') === 'cancel') showToast('Top-up canceled.', true);
})();

// AI usage strip — only shown for Pro accounts (Free has no AI quota to spend).
async function loadUsage() {
  const strip = document.getElementById('usageStrip');
  if (_mePlan !== 'pro') { strip.className = 'usage-strip'; return; }
  let usage;
  try { usage = await api('GET', '/api/billing/usage'); } catch (err) { return; }
  if (!usage || typeof usage.cap !== 'number') return;
  const used = usage.used || 0;
  const cap  = usage.cap  || 0;
  document.getElementById('usageUsed').textContent = used.toLocaleString();
  document.getElementById('usageCap').textContent  = cap.toLocaleString();
  const pct = cap > 0 ? Math.min(100, Math.round((used / cap) * 100)) : 0;
  document.getElementById('usageFill').style.width = pct + '%';
  let cls = 'usage-strip show';
  if (used >= cap)            cls += ' over';
  else if (used / cap >= 0.8) cls += ' warn';
  strip.className = cls;
  const msg = document.getElementById('usageMsg');
  if (used >= cap) msg.textContent = '🤖 AI replies paused. Top up to resume.';
  else             msg.textContent = 'Running low — top up to keep AI replies flowing.';
}

async function onTopupClick() {
  const btn = document.getElementById('usageTopupBtn');
  const original = btn.textContent;
  btn.disabled = true;
  btn.textContent = '…';
  try {
    // Off-session first: charges the saved card directly (no redirect). The
    // server returns 402 / { error: 'no_default_payment_method' | 'card_declined' }
    // when there's no usable card on file — fall back to the Checkout flow.
    const off = await api('POST', '/api/billing/topup-offsession', {});
    if (off && off.ok && off.off_session) {
      showToast('Top-up complete — +1000 AI replies added.');
      loadUsage();
      return;
    }
    const needsCheckout = !off || off.error === 'no_default_payment_method' ||
                          off.error === 'card_declined' || off.error === 'authentication_required';
    if (needsCheckout) {
      const resp = await api('POST', '/api/billing/topup', {});
      if (resp && resp.url) { location.href = resp.url; return; }
      showToast('Could not start top-up' + (resp && resp.error ? ': ' + resp.error : ''), true);
      return;
    }
    showToast('Could not complete top-up' + (off && off.error ? ': ' + off.error : ''), true);
  } catch (err) {
    showToast('Network error: ' + err.message, true);
  } finally {
    btn.disabled = false;
    btn.textContent = original;
  }
}

function escHtml(s) {
  return String(s == null ? '' : s)
    .replace(/&/g, '&amp;')
    .replace(/"/g, '&quot;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}

function buildEmbedSnippet(s) {
  // Mirrors the canonical snippet rendered by site_edit.php so a copy-paste
  // from EITHER place produces the same widget config.
  return '<script src="https://247ch.at/widget.js"\n' +
         '        data-site-id="' + s.id + '"\n' +
         '        data-site-name="' + escHtml(s.displayName || '') + '"\n' +
         '        data-starter-prompt="' + escHtml(s.starterPrompt || '') + '"><\/script>';
}

async function loadSites() {
  const sites = await api('GET', '/api/sites');
  const body = document.getElementById('sitesBody');
  if (!sites || !sites.length) {
    body.innerHTML = '<tr><td colspan="5" class="empty">No sites yet. Click "+ Add site" to start.</td></tr>';
    return;
  }
  body.innerHTML = sites.map(s => `
    <tr>
      <td>${escHtml(s.domain)}</td>
      <td><span class="swatch" style="background:${escHtml(s.primaryColor)}"></span>${escHtml(s.primaryColor)}</td>
      <td>${s.enabled ? 'Yes' : 'No'}</td>
      <td><button class="snip-toggle" onclick="toggleSnip(${s.id})">Show snippet</button></td>
      <td class="site-actions">
        <a href="/site_edit?id=${s.id}">Edit</a>
        <a href="/site_preview?id=${s.id}" target="_blank">Preview</a>
      </td>
    </tr>
    <tr id="snip-${s.id}" class="snip-row">
      <td colspan="5">
        <pre id="snip-pre-${s.id}">${escHtml(buildEmbedSnippet(s))}</pre>
        <button class="snip-copy" onclick="copySnip(${s.id})">Copy</button>
      </td>
    </tr>
  `).join('');
}

function toggleSnip(id) {
  document.getElementById('snip-' + id).classList.toggle('open');
}

function copySnip(id) {
  const text = document.getElementById('snip-pre-' + id).textContent;
  navigator.clipboard.writeText(text).then(() => showToast('Snippet copied.'))
    .catch(() => showToast('Copy failed — select manually.', true));
}

async function createSite() {
  const payload = {
    domain: document.getElementById('newDomain').value.trim(),
    displayName: document.getElementById('newName').value.trim(),
    primaryColor: document.getElementById('newColor').value,
    position: document.getElementById('newPosition').value,
    greetingText: document.getElementById('newGreeting').value.trim(),
  };
  await api('POST', '/api/sites', payload);
  document.getElementById('addModal').classList.remove('open');
  loadSites();
}

document.getElementById('signoutBtn').onclick = (e) => {
  e.preventDefault();
  window.__bearer = '';
  location.href = '/login.php';
};

loadMe().then(loadUsage); loadSites();

// Signup-wizard autostart: ?subscribe=monthly|annual & autostart=1 (set by
// signup.php after a successful POST or the Google-OAuth path). Also reads
// the PHP-session-staged plan from the Google-OAuth wizard via `staged_plan`.
(function() {
  const p = new URLSearchParams(location.search);
  const ALLOWED = { monthly: 1, annual: 1 };
  let plan = p.get('subscribe');
  const autostart = p.get('autostart') === '1';
  const staged = <?= json_encode($staged_plan) ?>;
  if (!plan && staged && ALLOWED[staged]) {
    plan = staged;
  }
  if (!plan || !ALLOWED[plan]) return;
  if (!autostart && !staged) return;
  // Strip query so reloads don't re-trigger checkout.
  history.replaceState(null, '', location.pathname);
  showToast('Starting ' + (plan === 'annual' ? 'Pro Annual ($199/year, auto-renews)' : 'Pro Monthly ($19/month, auto-renews)') + ' checkout…');
  // Defer one tick so loadMe() finishes (gives _mePlan a real value).
  setTimeout(() => { startCheckout(plan); }, 250);
})();
</script>

<?php include __DIR__ . '/includes/footer.php'; ?>
</body></html>
