<?php
// Сквозняк — дашборд учёта источников подписок Telegram.
// Данные берёт из витрины, которую публикует бот: ./data/dashboard.json (+ зеркало событий).
// Управление — через очередь заявок ./data/queue.jsonl: бот забирает её раз в 5 секунд
// и выполняет (создать/архивировать/удалить ссылку). Токен бота в веб-каталоге не хранится.
declare(strict_types=1);

ini_set('session.gc_maxlifetime', '7776000');
ini_set('session.cookie_lifetime', '7776000');
if (PHP_SAPI !== 'cli') {
    session_set_cookie_params(['lifetime' => 7776000, 'path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax']);
}
session_start();

$SECRETS = '/etc/quiz-secrets/skvoznyak.php';
$AUTH = is_file($SECRETS) ? require $SECRETS : ['login' => 'admin', 'hash' => ''];
$DATA = __DIR__ . '/data';
$SNAP = $DATA . '/dashboard.json';
$QUEUE = $DATA . '/queue.jsonl';
$RESULTS = $DATA . '/results.jsonl';
$EVENTS = $DATA . '/events.jsonl';
$TZ = 3 * 3600; // МСК

function h($v): string { return htmlspecialchars((string)($v ?? ''), ENT_QUOTES, 'UTF-8'); }

function jsonl_read(string $file, int $limit = 0): array {
    if (!is_file($file)) return [];
    $rows = [];
    $fh = fopen($file, 'r');
    if (!$fh) return [];
    while (($line = fgets($fh)) !== false) {
        $line = trim($line);
        if ($line === '') continue;
        $row = json_decode($line, true);
        if (is_array($row)) $rows[] = $row;
    }
    fclose($fh);
    if ($limit > 0 && count($rows) > $limit) $rows = array_slice($rows, -$limit);
    return $rows;
}

function fdt($ms): string { return $ms ? date('d.m.Y H:i', (int)($ms / 1000) + 3 * 3600) : '—'; }
function fday($ms): string { return $ms ? date('d.m.Y', (int)($ms / 1000) + 3 * 3600) : '—'; }
function nfmt($n): string { return number_format((float)$n, 0, ',', ' '); }

// ── выход
if (($_GET['a'] ?? '') === 'logout') { session_destroy(); header('Location: ./'); exit; }

// ── вход
$loginErr = '';
if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST' && ($_POST['do'] ?? '') === 'login') {
    $u = strtolower(trim((string)($_POST['login'] ?? '')));
    $p = (string)($_POST['password'] ?? '');
    if ($u === strtolower((string)$AUTH['login']) && $AUTH['hash'] !== '' && password_verify($p, (string)$AUTH['hash'])) {
        $_SESSION['sk'] = $u;
        header('Location: ./');
        exit;
    }
    $loginErr = 'Неверный логин или пароль';
    usleep(400000);
}
$auth = !empty($_SESSION['sk']);
$flash = '';
$flashErr = '';

// ── действия (только для вошедших)
if ($auth && ($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
    $do = (string)($_POST['do'] ?? '');
    $allowed = ['create_link', 'archive_link', 'delete_link', 'refresh'];
    if (in_array($do, $allowed, true)) {
        $req = ['id' => bin2hex(random_bytes(6)), 'action' => $do, 'ts' => (int)(microtime(true) * 1000)];
        if ($do === 'create_link') {
            $req['label'] = mb_substr(trim((string)($_POST['label'] ?? '')), 0, 28);
            if ((string)($_POST['member_limit'] ?? '') !== '') $req['member_limit'] = (int)$_POST['member_limit'];
            if ((string)($_POST['expire_hours'] ?? '') !== '') $req['expire_hours'] = (int)$_POST['expire_hours'];
            if ((string)($_POST['chat_id'] ?? '') !== '') $req['chat_id'] = (string)$_POST['chat_id'];
        } else {
            $req['code'] = (string)($_POST['code'] ?? '');
            $req['label'] = (string)($_POST['label'] ?? '');
        }
        if (is_writable($DATA)) {
            file_put_contents($QUEUE, json_encode($req, JSON_UNESCAPED_UNICODE) . "\n", FILE_APPEND | LOCK_EX);
            $flash = 'Заявка отправлена боту — через пару секунд обновите страницу.';
        } else {
            $flashErr = 'Нет прав на запись в каталог данных (обратитесь к администратору сервера).';
        }
    }
}

if ($auth && ($_GET['a'] ?? '') === 'export') {
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename="skvoznyak-events-' . date('Y-m-d') . '.csv"');
    $out = fopen('php://output', 'w');
    fwrite($out, "\xEF\xBB\xBF");
    fputcsv($out, ['дата (МСК)', 'канал', 'тип', 'имя', 'username', 'источник'], ';');
    $chMap = [];
    if (is_file($SNAP)) {
        $s = json_decode((string)file_get_contents($SNAP), true);
        foreach (($s['channels'] ?? []) as $c) {
            $chMap[(string)($c['id'] ?? '')] = (string)($c['title'] ?? '');
        }
    }
    foreach (jsonl_read($EVENTS, 0) as $e) {
        $ch = $chMap[(string)($e['chat_id'] ?? '')] ?? '—';
        fputcsv($out, [
            fdt($e['ts'] ?? 0),
            $ch,
            (($e['type'] ?? '') === 'join' ? 'подписка' : 'отписка'),
            (string)($e['name'] ?? ''),
            (string)($e['username'] ?? ''),
            (string)($e['link_name'] ?? '' ?: 'без ссылки'),
        ], ';');
    }
    fclose($out);
    exit;
}

$snap = is_file($SNAP) ? json_decode((string)file_get_contents($SNAP), true) : null;
if (!is_array($snap)) $snap = ['totals' => [], 'sources' => [], 'daily' => [], 'recent' => [], 'channels' => [], 'updated_at' => 0];
$results = array_slice(array_reverse(jsonl_read($RESULTS, 0)), 0, 8);

$tab = (string)($_GET['tab'] ?? 'overview');
if (!in_array($tab, ['overview', 'sources', 'events', 'settings'], true)) $tab = 'overview';

$t = $snap['totals'];
$j24 = (int)($t['joins_24h'] ?? 0);
$l24 = (int)($t['leaves_24h'] ?? 0);
$j7 = (int)($t['joins_7d'] ?? 0);
$l7 = (int)($t['leaves_7d'] ?? 0);
$j30 = (int)($t['joins_30d'] ?? 0);
$l30 = (int)($t['leaves_30d'] ?? 0);
$active = (int)($t['active'] ?? 0);
$avgStay = $t['avg_stay_days'] ?? null;
$channels = $snap['channels'] ?? [];
$sources = $snap['sources'] ?? [];
$daily = $snap['daily'] ?? [];
$recent = $snap['recent'] ?? [];
$updated = (int)($snap['updated_at'] ?? 0);
$conv = $snap['conversions'] ?? [];
$metrikaCounter = (int)($snap['metrika_counter'] ?? 0);
$hasData = !empty($recent) || !empty($sources);
?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex, nofollow">
<title>Сквозняк — источники подписок</title>
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
<style>
:root{
  --bg:#0f1115; --panel:#171a21; --panel2:#1d2129; --line:#282d38;
  --text:#e8eaf0; --muted:#9aa3b2; --accent:#4c8dff; --good:#37c98b; --bad:#ff6b6b; --warn:#f0b429;
}
*{box-sizing:border-box}
body{margin:0;background:var(--bg);color:var(--text);font:15px/1.5 -apple-system,"Segoe UI",Roboto,Arial,sans-serif}
a{color:var(--accent);text-decoration:none}
a:hover{text-decoration:underline}
.wrap{max-width:1180px;margin:0 auto;padding:24px 18px 60px}
header.top{display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap;margin-bottom:22px}
.brand{display:flex;align-items:center;gap:12px}
.brand .logo{width:38px;height:38px;border-radius:10px;background:linear-gradient(135deg,#4c8dff,#37c98b);display:flex;align-items:center;justify-content:center;font-weight:700;color:#0f1115}
.brand h1{font-size:19px;margin:0}
.brand .sub{color:var(--muted);font-size:13px}
.tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px}
.tabs a{padding:8px 14px;border-radius:9px;background:var(--panel);border:1px solid var(--line);color:var(--text);font-size:14px}
.tabs a.active{background:var(--accent);border-color:var(--accent);color:#fff}
.cards{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:12px;margin-bottom:22px}
.card{background:var(--panel);border:1px solid var(--line);border-radius:13px;padding:14px 16px}
.card .lbl{color:var(--muted);font-size:12.5px;margin-bottom:6px}
.card .val{font-size:26px;font-weight:600;letter-spacing:-.3px}
.card .val.good{color:var(--good)} .card .val.bad{color:var(--bad)}
.card .hint{color:var(--muted);font-size:12px;margin-top:4px}
.panel{background:var(--panel);border:1px solid var(--line);border-radius:14px;padding:18px;margin-bottom:20px}
.panel h2{font-size:16px;margin:0 0 14px}
table{width:100%;border-collapse:collapse;font-size:14px}
th,td{text-align:left;padding:9px 10px;border-bottom:1px solid var(--line);vertical-align:top}
th{color:var(--muted);font-weight:500;font-size:12.5px;text-transform:uppercase;letter-spacing:.04em}
tr:last-child td{border-bottom:none}
tr:hover td{background:var(--panel2)}
.num{text-align:right;font-variant-numeric:tabular-nums}
.pill{display:inline-block;padding:2px 9px;border-radius:20px;font-size:12px;border:1px solid var(--line);background:var(--panel2)}
.pill.good{color:var(--good);border-color:rgba(55,201,139,.4)}
.pill.bad{color:var(--bad);border-color:rgba(255,107,107,.4)}
.pill.mut{color:var(--muted)}
.mono{font-family:ui-monospace,Menlo,Consolas,monospace;font-size:12.5px;color:var(--muted);word-break:break-all}
.btn{display:inline-block;padding:9px 14px;border-radius:9px;border:1px solid var(--line);background:var(--panel2);color:var(--text);cursor:pointer;font-size:14px}
.btn:hover{border-color:var(--accent)}
.btn.primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn.small{padding:5px 10px;font-size:13px}
.btn.danger{color:var(--bad)}
input,select{background:var(--panel2);border:1px solid var(--line);color:var(--text);border-radius:9px;padding:9px 11px;font-size:14px;width:100%}
label{display:block;color:var(--muted);font-size:12.5px;margin:0 0 5px}
.grid2{display:grid;grid-template-columns:1.2fr .8fr;gap:20px}
.row{display:flex;gap:10px;flex-wrap:wrap;align-items:flex-end}
.row>div{flex:1 1 180px}
.flash{padding:10px 14px;border-radius:10px;margin-bottom:16px;font-size:14px}
.flash.ok{background:rgba(55,201,139,.12);border:1px solid rgba(55,201,139,.35);color:var(--good)}
.flash.err{background:rgba(255,107,107,.12);border:1px solid rgba(255,107,107,.35);color:var(--bad)}
.empty{color:var(--muted);font-size:14px;padding:10px 0}
.chart{width:100%;height:190px;display:block}
.legend{display:flex;gap:16px;color:var(--muted);font-size:12.5px;margin-top:8px}
.dot{display:inline-block;width:9px;height:9px;border-radius:50%;margin-right:6px}
.login{max-width:380px;margin:12vh auto}
.foot{color:var(--muted);font-size:12.5px;text-align:center;margin-top:26px}
@media(max-width:820px){.grid2{grid-template-columns:1fr}.cards{grid-template-columns:repeat(2,1fr)}}
</style>
</head>
<body>
<?php if (!$auth): ?>
<div class="wrap login">
  <div class="brand" style="margin-bottom:18px">
    <div class="logo">С</div>
    <div><h1>Сквозняк</h1><div class="sub">учёт источников подписок</div></div>
  </div>
  <div class="panel">
    <?php if ($loginErr !== ''): ?><div class="flash err"><?= h($loginErr) ?></div><?php endif; ?>
    <form method="post">
      <input type="hidden" name="do" value="login">
      <div style="margin-bottom:12px"><label>Логин</label><input name="login" autocomplete="username" autofocus></div>
      <div style="margin-bottom:16px"><label>Пароль</label><input name="password" type="password" autocomplete="current-password"></div>
      <button class="btn primary" style="width:100%">Войти</button>
    </form>
  </div>
</div>
<?php else: ?>
<div class="wrap">
  <header class="top">
    <div class="brand">
      <div class="logo">С</div>
      <div>
        <h1>Сквозняк — источники подписок</h1>
        <div class="sub">
          <?php if ($channels): foreach ($channels as $c): ?>
            <?= h($c['title']) ?><?= !empty($c['username']) ? ' (@' . h($c['username']) . ')' : '' ?> ·
          <?php endforeach; endif; ?>
          данные обновлены <?= h(fdt($updated)) ?> МСК
        </div>
      </div>
    </div>
    <div>
      <a class="btn small" href="?a=export">Скачать CSV</a>
      <a class="btn small" href="?a=logout">Выйти</a>
    </div>
  </header>

  <?php if ($flash !== ''): ?><div class="flash ok"><?= h($flash) ?></div><?php endif; ?>
  <?php if ($flashErr !== ''): ?><div class="flash err"><?= h($flashErr) ?></div><?php endif; ?>

  <?php if ($results): ?>
    <div class="panel">
      <h2>Ответы бота на заявки</h2>
      <?php foreach ($results as $r): ?>
        <div style="margin-bottom:6px;font-size:14px">
          <span class="pill <?= !empty($r['ok']) ? 'good' : 'bad' ?>"><?= !empty($r['ok']) ? 'готово' : 'ошибка' ?></span>
          <?= h($r['label'] ?? '') ?>
          <?php if (!empty($r['url'])): ?><span class="mono"><?= h($r['url']) ?></span><?php endif; ?>
          <?php if (!empty($r['error'])): ?>— <?= h($r['error']) ?><?php endif; ?>
          <?php if (!empty($r['note'])): ?>— <?= h($r['note']) ?><?php endif; ?>
          <span class="mono"><?= h(fdt($r['ts'] ?? 0)) ?></span>
        </div>
      <?php endforeach; ?>
    </div>
  <?php endif; ?>

  <nav class="tabs">
    <a class="<?= $tab === 'overview' ? 'active' : '' ?>" href="?tab=overview">Обзор</a>
    <a class="<?= $tab === 'sources' ? 'active' : '' ?>" href="?tab=sources">Источники и ссылки</a>
    <a class="<?= $tab === 'events' ? 'active' : '' ?>" href="?tab=events">События</a>
    <a class="<?= $tab === 'settings' ? 'active' : '' ?>" href="?tab=settings">Настройки</a>
  </nav>

<?php if ($tab === 'overview'): ?>
  <?php if (!$channels): ?>
    <div class="panel"><div class="empty">Канал пока не подключён. Добавьте бота @skvoznyak_gubtora_bot админом в канал — он подключится сам.</div></div>
  <?php endif; ?>

  <div class="cards">
    <div class="card"><div class="lbl">Подписки за 24 часа</div><div class="val good"><?= nfmt($j24) ?></div><div class="hint">за 7 дней: <?= nfmt($j7) ?> · за 30: <?= nfmt($j30) ?></div></div>
    <div class="card"><div class="lbl">Отписки за 24 часа</div><div class="val <?= $l24 > 0 ? 'bad' : '' ?>"><?= nfmt($l24) ?></div><div class="hint">за 7 дней: <?= nfmt($l7) ?> · за 30: <?= nfmt($l30) ?></div></div>
    <div class="card"><div class="lbl">Чистый прирост (24 ч)</div><div class="val <?= ($j24 - $l24) >= 0 ? 'good' : 'bad' ?>"><?= ($j24 - $l24) >= 0 ? '+' : '' ?><?= nfmt($j24 - $l24) ?></div><div class="hint">подписки минус отписки</div></div>
    <div class="card"><div class="lbl">Сейчас в канале</div><div class="val"><?= nfmt($active) ?></div><div class="hint">по данным учёта</div></div>
    <div class="card"><div class="lbl">Держатся в среднем</div><div class="val"><?= $avgStay === null ? '—' : h(number_format((float)$avgStay, 1, ',', ' ')) . ' дн.' ?></div><div class="hint">до отписки</div></div>
    <div class="card"><div class="lbl">Активных ссылок</div><div class="val"><?= nfmt((int)($t['links_total'] ?? 0)) ?></div><div class="hint">меток под источники</div></div>
  </div>

  <div class="panel">
    <h2>Динамика за 30 дней</h2>
    <?php
      $maxV = 1;
      foreach ($daily as $d) { $maxV = max($maxV, (int)$d['joins'], (int)$d['leaves']); }
      $w = 1080; $hh = 190; $pad = 26; $bw = (($w - 2 * $pad) / max(1, count($daily)));
    ?>
    <svg class="chart" viewBox="0 0 <?= $w ?> <?= $hh ?>" preserveAspectRatio="none">
      <line x1="<?= $pad ?>" y1="<?= $hh - $pad ?>" x2="<?= $w - $pad ?>" y2="<?= $hh - $pad ?>" stroke="#282d38" stroke-width="1"/>
      <?php foreach ($daily as $i => $d): ?>
        <?php
          $jv = (int)$d['joins']; $lv = (int)$d['leaves'];
          $xj = $pad + $i * $bw + $bw * 0.18;
          $xl = $pad + $i * $bw + $bw * 0.52;
          $hj = ($hh - 2 * $pad) * ($jv / $maxV);
          $hl = ($hh - 2 * $pad) * ($lv / $maxV);
        ?>
        <rect x="<?= round($xj, 2) ?>" y="<?= round($hh - $pad - $hj, 2) ?>" width="<?= round($bw * 0.3, 2) ?>" height="<?= round(max($hj, 0), 2) ?>" fill="#37c98b" rx="2"/>
        <rect x="<?= round($xl, 2) ?>" y="<?= round($hh - $pad - $hl, 2) ?>" width="<?= round($bw * 0.3, 2) ?>" height="<?= round(max($hl, 0), 2) ?>" fill="#ff6b6b" rx="2"/>
      <?php endforeach; ?>
    </svg>
    <div class="legend">
      <span><span class="dot" style="background:#37c98b"></span>подписки</span>
      <span><span class="dot" style="background:#ff6b6b"></span>отписки</span>
      <span style="margin-left:auto">максимум за день: <?= nfmt($maxV) ?></span>
    </div>
  </div>

  <div class="grid2">
    <div class="panel">
      <h2>Источники</h2>
      <?php if (!$sources): ?>
        <div class="empty">Пока нет данных. Создайте ссылку-метку во вкладке «Источники и ссылки» и поставьте её в рекламу.</div>
      <?php else: ?>
      <table>
        <tr><th>Источник</th><th class="num">Подписки</th><th class="num">Отписки</th><th class="num">Держатся</th></tr>
        <?php foreach (array_slice($sources, 0, 10) as $s): ?>
          <tr>
            <td><?= h($s['label'] ?? '') ?><?php if (!empty($s['archived'])): ?> <span class="pill mut">архив</span><?php endif; ?></td>
            <td class="num"><?= nfmt((int)($s['joins'] ?? 0)) ?></td>
            <td class="num"><?= nfmt((int)($s['leaves'] ?? 0)) ?></td>
            <td class="num"><?= ($s['retention'] ?? null) === null ? '—' : h($s['retention']) . '%' ?></td>
          </tr>
        <?php endforeach; ?>
      </table>
      <?php endif; ?>
    </div>
    <div class="panel">
      <h2>Последние события</h2>
      <?php if (!$recent): ?>
        <div class="empty">Событий пока нет.</div>
      <?php else: ?>
        <?php foreach (array_slice($recent, 0, 12) as $e): ?>
          <div style="padding:7px 0;border-bottom:1px solid var(--line);font-size:14px">
            <span class="pill <?= ($e['type'] ?? '') === 'join' ? 'good' : 'bad' ?>"><?= ($e['type'] ?? '') === 'join' ? 'подписался' : 'отписался' ?></span>
            <?= h($e['name'] ?? '') ?>
            <div class="mono"><?= h($e['source'] ?? '') ?> · <?= h(fdt($e['ts'] ?? 0)) ?></div>
          </div>
        <?php endforeach; ?>
      <?php endif; ?>
    </div>
  </div>

  <div class="panel">
    <h2>Конверсии в Яндекс.Метрику<?= $metrikaCounter ? ' · счётчик ' . $metrikaCounter : '' ?></h2>
    <div class="cards" style="margin-bottom:14px">
      <div class="card"><div class="lbl">Передано конверсий</div><div class="val good"><?= nfmt((int)($conv['ok'] ?? 0)) ?></div><div class="hint">цель «Подписка на канал»</div></div>
      <div class="card"><div class="lbl">Не передано</div><div class="val <?= (int)($conv['failed'] ?? 0) > 0 ? 'bad' : '' ?>"><?= nfmt((int)($conv['failed'] ?? 0)) ?></div><div class="hint">причина видна ниже</div></div>
      <div class="card"><div class="lbl">Всего событий</div><div class="val"><?= nfmt((int)($conv['total'] ?? 0)) ?></div><div class="hint">по всем источникам</div></div>
    </div>
    <?php if (empty($conv['last'])): ?>
      <div class="empty">
        Пока конверсий нет. Они появляются так: человек переходит по рекламной ссылке вида
        <span class="mono">gubtor.ru/go/&lt;метка&gt;</span>, мы запоминаем его ClientID (или yclid из объявления Директа),
        выдаём разовую ссылку в канал — и когда он подписывается, конверсия уходит в Метрику автоматически.
      </div>
    <?php else: ?>
      <table>
        <tr><th>Время</th><th>Человек</th><th>Источник</th><th>Способ</th><th>Кто в Метрике</th><th>Статус</th></tr>
        <?php foreach ($conv['last'] as $c):
            $cid = (string)($c['client_id'] ?? '');
            $ycl = (string)($c['yclid'] ?? '');
            if ($cid !== '') { $who = 'ClientID ' . $cid; }
            elseif ($ycl !== '') { $who = 'yclid ' . $ycl; }
            else { $who = '—'; }
            $method = (string)($c['method'] ?? '');
            if ($method === 'mp') { $mLabel = 'быстрый (~20 мин)'; }
            elseif ($method === 'offline') { $mLabel = 'офлайн-загрузка'; }
            else { $mLabel = '—'; }
        ?>
          <tr>
            <td class="mono"><?= h(fdt($c['ts'] ?? 0)) ?></td>
            <td><?= h($c['user'] ?? '') ?></td>
            <td><?= h($c['label'] ?? '') ?></td>
            <td><span class="pill mut"><?= h($mLabel) ?></span></td>
            <td class="mono"><?= h($who) ?></td>
            <td><span class="pill <?= !empty($c['ok']) ? 'good' : 'bad' ?>"><?= !empty($c['ok']) ? 'передана' : 'ошибка' ?></span><?= !empty($c['error']) ? ' <span class="mono">' . h($c['error']) . '</span>' : '' ?></td>
          </tr>
        <?php endforeach; ?>
      </table>
    <?php endif; ?>
  </div>

<?php elseif ($tab === 'sources'): ?>
  <div class="panel">
    <h2>Создать ссылку-метку под источник</h2>
    <form method="post">
      <input type="hidden" name="do" value="create_link">
      <div class="row">
        <div style="flex:2 1 320px"><label>Название источника (увидите в отчётах)</label><input name="label" placeholder="Реклама у Ивана · Посев в чате Баня-форум" maxlength="28" required></div>
        <?php if (count($channels) > 1): ?>
        <div><label>Канал</label><select name="chat_id"><?php foreach ($channels as $c): ?><option value="<?= h($c['id']) ?>"><?= h($c['title']) ?></option><?php endforeach; ?></select></div>
        <?php endif; ?>
        <div><label>Лимит человек (необязательно)</label><input name="member_limit" type="number" min="1" placeholder="напр. 100"></div>
        <div><label>Срок жизни, часов (необязательно)</label><input name="expire_hours" type="number" min="1" placeholder="напр. 168"></div>
        <div style="flex:0 0 auto"><button class="btn primary">Создать ссылку</button></div>
      </div>
    </form>
    <div class="empty" style="margin-top:12px">Название вписывайте так, как вам удобно читать в отчётах: «Реклама у Ивана», «Посевы в чатах», «Сторис», «Пост про бани». Именно оно попадёт в отчёт источником.</div>
  </div>

  <div class="panel">
    <h2>Все ссылки и источники</h2>
    <?php if (!$sources): ?>
      <div class="empty">Ссылок пока нет.</div>
    <?php else: ?>
    <table>
      <tr><th>Источник</th><th class="num">Подписки</th><th class="num">Отписки</th><th class="num">Держатся</th><th>Последняя активность</th><th></th></tr>
      <?php foreach ($sources as $s): ?>
        <tr>
          <td>
            <?= h($s['label'] ?? '') ?>
            <?php if (!empty($s['archived'])): ?> <span class="pill mut">архив</span><?php endif; ?>
            <?php if (!empty($s['label']) && ($s['label'] ?? '') !== 'без ссылки'): ?>
              <div class="mono copy" data-copy="<?= h('https://gubtor.ru/go/' . rawurlencode((string)$s['label'])) ?>" style="cursor:pointer" title="Нажмите, чтобы скопировать рекламную ссылку">gubtor.ru/go/<?= h($s['label']) ?> <span class="pill mut">рекламная ссылка</span></div>
            <?php endif; ?>
            <?php if (!empty($s['code'])): ?><div class="mono copy" data-copy="<?= h($s['code']) ?>" style="cursor:pointer" title="Нажмите, чтобы скопировать прямую ссылку"><?= h($s['code']) ?></div><?php endif; ?>
          </td>
          <td class="num"><?= nfmt((int)($s['joins'] ?? 0)) ?></td>
          <td class="num"><?= nfmt((int)($s['leaves'] ?? 0)) ?></td>
          <td class="num"><?= ($s['retention'] ?? null) === null ? '—' : h($s['retention']) . '%' ?></td>
          <td class="mono"><?= h($s['last_ts'] ? fdt($s['last_ts']) : '—') ?></td>
          <td style="white-space:nowrap">
            <?php if (!empty($s['code'])): ?>
              <form method="post" style="display:inline">
                <input type="hidden" name="do" value="archive_link">
                <input type="hidden" name="code" value="<?= h($s['code']) ?>">
                <button class="btn small">В архив</button>
              </form>
              <form method="post" style="display:inline" onsubmit="return confirm('Удалить ссылку? Она перестанет работать, статистика по источнику обнулится.')">
                <input type="hidden" name="do" value="delete_link">
                <input type="hidden" name="code" value="<?= h($s['code']) ?>">
                <input type="hidden" name="label" value="<?= h($s['label'] ?? '') ?>">
                <button class="btn small danger">Удалить</button>
              </form>
            <?php else: ?>
              <span class="pill mut">внешние переходы</span>
            <?php endif; ?>
          </td>
        </tr>
      <?php endforeach; ?>
    </table>
    <?php endif; ?>
  </div>

<?php elseif ($tab === 'events'): ?>
  <div class="panel">
    <h2>События (последние 200)</h2>
    <?php if (!$recent): ?>
      <div class="empty">Событий пока нет.</div>
    <?php else: ?>
    <table>
      <tr><th>Время (МСК)</th><th>Тип</th><th>Человек</th><th>Источник</th></tr>
      <?php foreach ($recent as $e): ?>
        <tr>
          <td class="mono"><?= h(fdt($e['ts'] ?? 0)) ?></td>
          <td><span class="pill <?= ($e['type'] ?? '') === 'join' ? 'good' : 'bad' ?>"><?= ($e['type'] ?? '') === 'join' ? 'подписка' : 'отписка' ?></span></td>
          <td><?= h($e['name'] ?? '') ?><?= !empty($e['username']) ? ' <span class="mono">@' . h($e['username']) . '</span>' : '' ?></td>
          <td><?= h($e['source'] ?? '') ?></td>
        </tr>
      <?php endforeach; ?>
    </table>
    <div class="empty" style="margin-top:10px">Полная выгрузка всех событий — кнопка «Скачать CSV» сверху.</div>
    <?php endif; ?>
  </div>

<?php elseif ($tab === 'settings'): ?>
  <div class="panel">
    <h2>Подключённые каналы</h2>
    <?php if (!$channels): ?>
      <div class="empty">Ни одного канала. Добавьте бота @skvoznyak_gubtora_bot админом в канал — он подключится автоматически и пришлёт подтверждение.</div>
    <?php else: ?>
    <table>
      <tr><th>Канал</th><th>ID</th><th class="num">Подписки</th><th class="num">Отписки</th><th class="num">За 24 ч</th><th>Подключён</th></tr>
      <?php foreach ($channels as $c): ?>
        <tr>
          <td><?= h($c['title'] ?? '') ?><?= !empty($c['username']) ? ' <span class="mono">@' . h($c['username']) . '</span>' : '' ?></td>
          <td class="mono"><?= h($c['id'] ?? '') ?></td>
          <td class="num"><?= nfmt((int)($c['joins'] ?? 0)) ?></td>
          <td class="num"><?= nfmt((int)($c['leaves'] ?? 0)) ?></td>
          <td class="num"><?= nfmt((int)($c['joins_24h'] ?? 0)) ?></td>
          <td class="mono"><?= h(fdt($c['added_at'] ?? 0)) ?></td>
        </tr>
      <?php endforeach; ?>
    </table>
    <?php endif; ?>
  </div>
  <div class="panel">
    <h2>Как это работает</h2>
    <div class="empty">
      1. Бот подключён к каналу админом — Telegram сообщает ему о каждом входе и выходе и указывает, по какой ссылке человек вошёл.<br>
      2. Для рекламы используйте <b>рекламную ссылку</b> вида <span class="mono">gubtor.ru/go/&lt;метка&gt;</span>: она запоминает посетителя в Метрике (ClientID или yclid из объявления) и выдаёт ему разовую пригласительную ссылку.<br>
      3. Когда человек по ней подписывается, бот отправляет в Метрику конверсию «Подписка на канал» — Директ сможет учиться на реальных подписках, а не на кликах.<br>
      4. Прямая пригласительная ссылка (t.me/+) тоже работает и считается в статистике, но конверсию в Метрику по ней передать нельзя: Telegram не отдаёт нам, кто именно перешёл.<br>
      5. Каждый день в 08:15 МСК бот присылает отчёт в Telegram; здесь всегда актуальная статистика и управление ссылками.<br>
      6. Источники «без ссылки» — те, кто пришёл из поиска Telegram, по репостам или из профиля: источник Telegram в этом случае не сообщает.
    </div>
  </div>
<?php endif; ?>

  <div class="foot">Сквозняк · данные обновляются ботом каждую минуту и сразу после каждого события</div>
</div>

<script>
document.querySelectorAll('.copy').forEach(function (el) {
  el.addEventListener('click', function () {
    var text = el.getAttribute('data-copy') || '';
    var done = function () {
      var old = el.getAttribute('data-old') || el.textContent;
      el.setAttribute('data-old', old);
      el.textContent = 'Скопировано';
      setTimeout(function () { el.textContent = old; }, 1800);
    };
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(text).then(done).catch(function () { fallback(text, done); });
    } else { fallback(text, done); }
  });
});
function fallback(text, done) {
  var ta = document.createElement('textarea');
  ta.value = text; document.body.appendChild(ta); ta.select();
  try { document.execCommand('copy'); done(); } catch (e) {}
  document.body.removeChild(ta);
}
</script>
<?php endif; ?>
</body>
</html>
