<?php
// =============================================================================
// debug.php — support-mode diagnostic dump.
//
// Not linked from the sidebar. Buyers never see it. Reseller/vendor hits
// this URL with a support token to get everything they'd otherwise need
// to ask over chat: PHP version + extensions, ini limits, file permissions
// on critical paths, DB stats, recent request log, recent panel error log.
//
// Usage:
//   https://buyer.com/panel/debug.php?key=<SUPPORT_TOKEN>
//
// Token lives in includes/debug_constants.php (bundled). Change it there
// if you ever need to rotate.
//
// Fails closed: any bad/missing token returns a plain 404 with no body —
// buyer can't tell the endpoint exists.
// =============================================================================

$constants = @include __DIR__ . '/includes/debug_constants.php';
$expected  = is_array($constants) ? (string)($constants['SUPPORT_TOKEN'] ?? '') : '';
$got       = (string)($_GET['key'] ?? '');

if ($expected === '' || !hash_equals($expected, $got)) {
    header('HTTP/1.1 404 Not Found');
    exit;
}

// Output format: html (default, interactive) | txt (plain report, copy-paste friendly)
$format = (string)($_GET['format'] ?? 'html');
if (!in_array($format, ['html', 'txt'], true)) $format = 'html';

require_once __DIR__ . '/includes/config.php';
require_once __DIR__ . '/includes/db.php';

// ---- Export panel state (GET, streams a zip) -----------------------------
// Wraps panel.db + a metadata.json (host, timestamp, apksec sanity) so the
// reseller can `unzip` it locally and load the exact state their buyer has.
if (($_GET['action'] ?? '') === 'export') {
    $tmp = tempnam(sys_get_temp_dir(), 'panelexp_');
    $z = new ZipArchive();
    if ($z->open($tmp, ZipArchive::OVERWRITE) !== true) {
        header('HTTP/1.1 500 Internal Server Error');
        echo 'zip open failed'; @unlink($tmp); exit;
    }
    $db_path = __DIR__ . '/data/panel.db';
    if (is_file($db_path)) $z->addFile($db_path, 'panel.db');
    $err_path = __DIR__ . '/data/panel.error.log';
    if (is_file($err_path)) $z->addFile($err_path, 'panel.error.log');
    $meta = [
        'exported_at' => date('c'),
        'host'        => $_SERVER['HTTP_HOST'] ?? 'unknown',
        'panel_root'  => realpath(__DIR__),
        'php_version' => PHP_VERSION,
        'apksec_variant'  => (is_file(__DIR__ . '/includes/apksec_constants.php')
            && strpos(file_get_contents(__DIR__ . '/includes/apksec_constants.php'), 'RESELLER VARIANT') !== false)
            ? 'reseller' : 'customer/other',
        'crypto_has_tail' => is_file(__DIR__ . '/includes/crypto.php')
            ? (substr_count(file_get_contents(__DIR__ . '/includes/crypto.php'), 'random_bytes(32)') > 0 ? 'yes' : 'no')
            : '?',
    ];
    $z->addFromString('metadata.json', json_encode($meta, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
    $z->close();
    $host = preg_replace('/[^a-z0-9]+/', '-', strtolower($_SERVER['HTTP_HOST'] ?? 'host'));
    $fname = 'panel-state-' . $host . '-' . date('Ymd-His') . '.zip';
    header('Content-Type: application/zip');
    header('Content-Disposition: attachment; filename="' . $fname . '"');
    header('Content-Length: ' . filesize($tmp));
    readfile($tmp);
    @unlink($tmp);
    exit;
}

// ---- POST actions (log clearing) -----------------------------------------
// Guarded by the same support token that gated this endpoint above.
if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    header('Content-Type: application/json; charset=utf-8');
    $action = (string)($_POST['action'] ?? '');
    if ($action === 'clear_errors') {
        $p = __DIR__ . '/data/panel.error.log';
        if (is_file($p)) {
            $ok = @file_put_contents($p, '') !== false;
            echo json_encode(['ok' => $ok, 'action' => 'clear_errors']);
        } else {
            echo json_encode(['ok' => true, 'note' => 'no log file existed']);
        }
        exit;
    }
    if ($action === 'clear_request_log') {
        try {
            $n = 0;
            $pdo = panel_db();
            $pdo->exec('DELETE FROM panel_request_log');
            $n = (int)$pdo->query('SELECT changes()')->fetchColumn();
            echo json_encode(['ok' => true, 'deleted' => $n]);
        } catch (Throwable $e) {
            http_response_code(500);
            echo json_encode(['ok' => false, 'error' => $e->getMessage()]);
        }
        exit;
    }
    if ($action === 'clear_all') {
        // both — one click after a support session ends
        $p = __DIR__ . '/data/panel.error.log';
        if (is_file($p)) @file_put_contents($p, '');
        try {
            panel_db()->exec('DELETE FROM panel_request_log');
        } catch (Throwable $e) {}
        echo json_encode(['ok' => true, 'action' => 'clear_all']);
        exit;
    }
    http_response_code(400);
    echo json_encode(['ok' => false, 'error' => 'unknown action']);
    exit;
}

function _bytes($n) {
    if ($n === false || $n === null) return '—';
    $n = (float)$n;
    foreach (['B','KB','MB','GB','TB'] as $u) {
        if ($n < 1024) return sprintf('%.1f %s', $n, $u);
        $n /= 1024;
    }
    return sprintf('%.1f PB', $n);
}
function _perm($path) {
    if (!file_exists($path)) return ['—', '—', '—', '—'];
    $s = @stat($path);
    if (!$s) return ['—', '—', '—', '—'];
    $mode = decoct($s['mode'] & 0777);
    $owner = function_exists('posix_getpwuid') ? posix_getpwuid($s['uid'])['name'] ?? $s['uid'] : $s['uid'];
    $group = function_exists('posix_getgrgid') ? posix_getgrgid($s['gid'])['name'] ?? $s['gid'] : $s['gid'];
    $write = is_writable($path) ? 'yes' : 'NO';
    return [$mode, $owner . ':' . $group, $write, is_dir($path) ? 'dir' : 'file'];
}
function _h($s) { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }

$panel_root = realpath(__DIR__);

// ---- Collect ----
$php = [
    'version'     => PHP_VERSION,
    'sapi'        => php_sapi_name(),
    'os'          => PHP_OS,
    'user'        => function_exists('posix_getpwuid') ? (posix_getpwuid(posix_geteuid())['name'] ?? '?') : '?',
    'timezone'    => date_default_timezone_get(),
    'server_time' => date('c'),
];
// Required = panel won't run correctly without them.
// Optional = nice-to-have; panel has fallbacks.
$req_ext = ['pdo_sqlite', 'sqlite3', 'openssl', 'curl', 'gd', 'mbstring', 'json', 'zip'];
$opt_ext = ['fileinfo'];   // getimagesize() fallback in place, panel still works without
$ext_status = [];
foreach ($req_ext as $e) $ext_status[$e] = ['loaded' => extension_loaded($e), 'required' => true];
foreach ($opt_ext as $e) $ext_status[$e] = ['loaded' => extension_loaded($e), 'required' => false];

$ini = [];
foreach ([
    'memory_limit', 'max_execution_time', 'post_max_size', 'upload_max_filesize',
    'max_file_uploads', 'file_uploads', 'allow_url_fopen', 'display_errors',
    'session.save_path', 'default_charset', 'date.timezone',
] as $k) {
    $ini[$k] = ini_get($k);
}

$paths = [
    __DIR__ . '/data',
    __DIR__ . '/data/panel.db',
    __DIR__ . '/data/panel.error.log',
    __DIR__ . '/uploads',
    __DIR__ . '/uploads/slideshows',
    __DIR__ . '/.htaccess',
    __DIR__ . '/includes/apksec_constants.php',
    __DIR__ . '/includes/crypto.php',
    __DIR__ . '/includes/addaplaylist_constants.php',
    __DIR__ . '/cron/addaplaylist_register.php',
];

$db_stats = [];
try {
    $pdo = panel_db();
    $tables = ['devices', 'playlists', 'vpn_profiles', 'portals', 'webview_slideshows',
               'webview_settings', 'admin_users', 'panel_request_log', 'guard_key_log',
               'bootstrap_log'];
    foreach ($tables as $t) {
        try {
            $c = (int)$pdo->query('SELECT COUNT(*) FROM ' . $t)->fetchColumn();
            $db_stats[$t] = $c;
        } catch (Throwable $e) {
            $db_stats[$t] = '(missing)';
        }
    }
    $cfg = $pdo->query('SELECT slug, addaplaylist_panel_code, substr(addaplaylist_auth_token,1,8) AS tok, panel_base_url FROM config LIMIT 1')->fetch(PDO::FETCH_ASSOC);
} catch (Throwable $e) {
    $cfg = ['(db error)' => $e->getMessage()];
}

// Recent request log — last 15 hits
$recent_req = [];
try {
    $recent_req = $pdo->query(
        "SELECT datetime(created_at,'unixepoch') AS t, method, path, response_status AS s, ip_address AS ip
         FROM panel_request_log ORDER BY id DESC LIMIT 15"
    )->fetchAll(PDO::FETCH_ASSOC);
} catch (Throwable $e) {}

// Recent panel error log entries — read last ~20 lines by tailing the file.
$panel_errs = [];
$err_path = __DIR__ . '/data/panel.error.log';
if (is_file($err_path)) {
    $size = (int)@filesize($err_path);
    if ($size > 0) {
        $chunk = min($size, 32 * 1024);
        $fh = @fopen($err_path, 'r');
        if ($fh) {
            @fseek($fh, -$chunk, SEEK_END);
            $tail = @fread($fh, $chunk);
            fclose($fh);
            $rows = array_filter(array_map('trim', explode("\n", (string)$tail)));
            foreach (array_slice($rows, -20) as $r) {
                $j = @json_decode($r, true);
                if (is_array($j)) $panel_errs[] = $j;
            }
        }
    }
}

// Reseller apksec / crypto sanity
$reseller_check = ['apksec_variant' => '?', 'crypto_has_tail' => '?'];
$apk_const = __DIR__ . '/includes/apksec_constants.php';
if (is_file($apk_const)) {
    $reseller_check['apksec_variant'] = strpos(file_get_contents($apk_const), 'RESELLER VARIANT') !== false
        ? 'reseller' : 'customer/other';
}
$crypto = __DIR__ . '/includes/crypto.php';
if (is_file($crypto)) {
    $reseller_check['crypto_has_tail'] = substr_count(file_get_contents($crypto), 'random_bytes(32)') > 0
        ? 'YES (bug — should be reseller-variant with no tail)' : 'no (reseller-variant OK)';
}
// ---- Plain-text report (for copy-paste into chat / email) ----------------
if ($format === 'txt') {
    header('Content-Type: text/plain; charset=utf-8');
    $slug = $cfg['slug'] ?? '';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $out  = [];
    $out[] = str_repeat('=', 68);
    $out[] = 'Panel diagnostic — ' . date('c');
    $out[] = 'Host: ' . $host . ($slug ? ' · slug=' . $slug : '');
    $out[] = str_repeat('=', 68);
    $out[] = '';

    $out[] = '[ PHP ]';
    foreach ($php as $k => $v) $out[] = sprintf('  %-14s %s', $k, $v);
    $out[] = '';

    $out[] = '[ Extensions ]';
    foreach ($ext_status as $e => $info) {
        $tag = $info['loaded'] ? 'yes' : ($info['required'] ? 'MISSING' : 'missing (optional)');
        $out[] = sprintf('  %-14s %s', $e, $tag);
    }
    $out[] = '';

    $out[] = '[ ini settings ]';
    foreach ($ini as $k => $v) $out[] = sprintf('  %-24s %s', $k, $v);
    $out[] = '';

    $out[] = '[ File permissions ]';
    foreach ($paths as $p) {
        [$m, $og, $w, $t] = _perm($p);
        $rel = str_replace($panel_root, '.', $p);
        $out[] = sprintf('  %-40s %-5s %-15s writable=%-3s (%s)', $rel, $m, $og, $w, $t);
    }
    $out[] = '';

    $out[] = '[ Reseller sanity ]';
    foreach ($reseller_check as $k => $v) $out[] = sprintf('  %-24s %s', $k, $v);
    $out[] = '';

    $out[] = '[ panel.db table counts ]';
    foreach ($db_stats as $t => $c) $out[] = sprintf('  %-24s %s', $t, $c);
    $out[] = '';

    $out[] = '[ config row ]';
    foreach ($cfg as $k => $v) $out[] = sprintf('  %-24s %s', $k, $v);
    $out[] = '';

    $out[] = '[ Recent request log ]';
    if ($recent_req) {
        foreach ($recent_req as $r) {
            $out[] = sprintf('  %s  %-5s %-4d %-32s %s',
                $r['t'], $r['method'], $r['s'], substr((string)$r['path'], 0, 32), $r['ip']);
        }
    } else {
        $out[] = '  (empty)';
    }
    $out[] = '';

    $out[] = '[ Recent panel errors ]';
    if ($panel_errs) {
        foreach (array_reverse($panel_errs) as $e) {
            $out[] = ($e['ts_iso'] ?? '?') . '  ' . ($e['level'] ?? '') . '  ' . ($e['message'] ?? '');
            if (!empty($e['file'])) $out[] = '    at ' . $e['file'] . ':' . ($e['line'] ?? '');
            if (!empty($e['url']))  $out[] = '    hit ' . ($e['method'] ?? '') . ' ' . $e['url'];
        }
    } else {
        $out[] = '  (empty)';
    }
    $out[] = '';
    $out[] = 'end of report';

    echo implode("\n", $out);
    exit;
}
// ---- Top-of-page overall health pill ------------------------------------
// Only count REQUIRED extensions in the missing tally; optional ones warn only.
$missing_req_ext = 0; $missing_opt_ext = 0;
foreach ($ext_status as $info) {
    if ($info['loaded']) continue;
    if ($info['required']) $missing_req_ext++; else $missing_opt_ext++;
}
$missing_ext = $missing_req_ext;   // used by later code that expects a single count
$wrong_variant = $reseller_check['apksec_variant'] !== 'reseller'
              || strpos((string)$reseller_check['crypto_has_tail'], 'YES') === 0;
$bad_perms = 0;
foreach ($paths as $p) { $r = _perm($p); if ($r[2] !== 'yes' && $r[0] !== '—' && strpos($p, '.php') === false) $bad_perms++; }
$errcount = count($panel_errs);
if ($wrong_variant || $missing_ext > 0 || $bad_perms > 0 || $errcount > 3) {
    $health = ['label' => 'ATTENTION', 'color' => '#ef4444', 'bg' => '#7f1d1d33'];
} elseif ($errcount > 0) {
    $health = ['label' => 'MINOR ISSUES', 'color' => '#f39c12', 'bg' => '#78350f33'];
} else {
    $health = ['label' => 'HEALTHY', 'color' => '#22c55e', 'bg' => '#14532d33'];
}

$here_url = ($_SERVER['REQUEST_SCHEME'] ?? 'https') . '://' . ($_SERVER['HTTP_HOST'] ?? '')
          . strtok($_SERVER['REQUEST_URI'] ?? '/debug.php', '?');
$key      = urlencode($got);
$txt_url  = $here_url . '?key=' . $key . '&format=txt';
$dl_name  = 'panel-debug-' . preg_replace('/[^a-z0-9]+/', '-', strtolower($_SERVER['HTTP_HOST'] ?? 'host')) . '-' . date('Ymd-His') . '.txt';
?><!doctype html>
<html><head><meta charset="utf-8"><title>Panel Debug — <?php echo _h($_SERVER['HTTP_HOST'] ?? ''); ?></title>
<style>
:root{--bg:#0a0a0a;--card:#141414;--card2:#1a1a1a;--line:#262626;--muted:#666;--text:#e2e8f0;--dim:#94a3b8;--accent:#f39c12}
*{box-sizing:border-box}
html,body{margin:0;padding:0;background:var(--bg);color:var(--text);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}
a{color:inherit}

/* Sticky header */
.stickytop{position:sticky;top:0;z-index:50;background:linear-gradient(180deg,#0a0a0a 60%,rgba(10,10,10,0.85));backdrop-filter:blur(8px);padding:14px 24px;border-bottom:1px solid var(--line)}
.title-row{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:10px}
h1{color:var(--accent);font-size:18px;margin:0;font-weight:700}
.pill{padding:3px 10px;border-radius:14px;font-weight:700;font-size:10px;letter-spacing:.05em}
.host{color:var(--dim);font-size:11px}
.grow{flex:1}
.gen{font-size:10px;color:var(--muted)}

/* Button rows */
.btnbar{display:flex;gap:6px;flex-wrap:wrap}
.btnbar+.btnbar{margin-top:6px}
.btn{background:var(--accent);color:#000;padding:6px 12px;border-radius:4px;font-weight:600;text-decoration:none;font-size:11px;border:0;cursor:pointer;font-family:inherit;white-space:nowrap;line-height:1.4}
.btn.secondary{background:#1e1e1e;color:var(--text);border:1px solid #2a2a2a}
.btn.danger{background:#7f1d1d;color:#fff;border-color:#7f1d1d}
.btn:hover{filter:brightness(1.1)}
.btn:disabled{opacity:.5;cursor:default}

/* Page body */
.body{padding:18px 24px 48px;max-width:1400px;margin:0 auto}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:800px){.grid2{grid-template-columns:1fr}}

/* Card */
.card{background:var(--card);border:1px solid var(--line);border-radius:6px;margin-bottom:14px;overflow:hidden}
.card-head{display:flex;align-items:center;justify-content:space-between;background:var(--card2);padding:8px 12px;border-bottom:1px solid var(--line)}
.card-head h2{color:var(--accent);font-size:12px;margin:0;font-weight:700;letter-spacing:.02em;display:flex;align-items:center;gap:8px}
.card-head .count{background:#0a0a0a;padding:2px 8px;border-radius:10px;font-size:10px;color:var(--dim)}
.card-body{padding:2px 12px 10px}
.card-copybtn{background:transparent;border:1px solid #2a2a2a;color:var(--dim);padding:2px 8px;border-radius:3px;font-size:10px;cursor:pointer;font-family:inherit}
.card-copybtn:hover{color:var(--text);border-color:#444}

/* Card status bar */
.card.status-warn{border-color:#78350f}
.card.status-warn .card-head{background:#78350f22}
.card.status-bad{border-color:#7f1d1d}
.card.status-bad .card-head{background:#7f1d1d22}
.card.status-ok .card-head h2::before{content:"●";color:#22c55e;font-size:10px;margin-right:0}

/* Tables */
table{border-collapse:collapse;width:100%;margin:0}
th,td{padding:5px 8px;text-align:left;border-bottom:1px solid var(--line);vertical-align:top}
tr:last-child td{border-bottom:0}
th{color:var(--muted);font-weight:600;font-size:10px;text-transform:uppercase;letter-spacing:.05em}

/* Value cells */
.k{color:var(--dim);white-space:nowrap}
.v{color:var(--text)}
.ok{color:#22c55e}.bad{color:#ef4444}.warn{color:#f39c12}
.small{color:var(--muted);font-size:10px}
.chip{display:inline-block;padding:1px 6px;border-radius:3px;font-size:10px;background:#0a0a0a;border:1px solid #2a2a2a}
.chip.ok{background:#14532d33;border-color:#166534;color:#22c55e}
.chip.bad{background:#7f1d1d33;border-color:#7f1d1d;color:#fca5a5}
.chip.warn{background:#78350f33;border-color:#a16207;color:#fbbf24}

/* Empty state */
.empty{padding:14px;color:var(--muted);text-align:center;font-size:11px;font-style:italic}
.empty.ok{color:#22c55e;font-style:normal}

/* Error entry */
.errcard{background:#0a0a0a;border-left:3px solid #ef4444;padding:8px 10px;margin:6px 0;border-radius:0 3px 3px 0}
.errcard .ts{color:var(--muted);font-size:10px}
.errcard .msg{color:#fca5a5;margin:2px 0;word-break:break-word}
.errcard .meta{color:var(--dim);font-size:10px;margin-top:2px}

/* Section anchor scroll offset for sticky top */
section{scroll-margin-top:120px}
</style></head><body>

<div class="stickytop">
    <div class="title-row">
        <h1>Panel Debug</h1>
        <span class="pill" style="color:<?php echo $health['color']; ?>;background:<?php echo $health['bg']; ?>;border:1px solid <?php echo $health['color']; ?>"><?php echo _h($health['label']); ?></span>
        <span class="host"><?php echo _h($_SERVER['HTTP_HOST'] ?? ''); ?><?php echo _h(isset($cfg['slug']) && $cfg['slug'] ? ' · ' . $cfg['slug'] : ''); ?></span>
        <span class="grow"></span>
        <label class="small" style="display:flex;align-items:center;gap:6px;cursor:pointer">
            <input type="checkbox" id="autoref"> auto-refresh (10s)
        </label>
        <span class="gen">gen <?php echo date('H:i:s'); ?></span>
    </div>
    <div class="btnbar">
        <button class="btn" onclick="copyReport(this)">Copy report</button>
        <a class="btn secondary" href="<?php echo _h($txt_url); ?>" download="<?php echo _h($dl_name); ?>">Download .txt</a>
        <a class="btn secondary" href="<?php echo _h($txt_url); ?>" target="_blank">Open plain text</a>
        <a class="btn secondary" href="<?php echo _h($here_url . '?key=' . $key . '&action=export'); ?>">Export panel state (.zip)</a>
        <span class="grow"></span>
        <button class="btn secondary" onclick="clearLog('clear_errors', this)">Clear errors</button>
        <button class="btn secondary" onclick="clearLog('clear_request_log', this)">Clear requests</button>
        <button class="btn danger" onclick="clearLog('clear_all', this)">Clear BOTH</button>
    </div>
</div>

<div class="body">

<?php
// Small helper for consistent table rendering
function _kvtable($rows, $narrow_key = true) {
    echo '<table>';
    foreach ($rows as $k => $v) {
        $vs = is_bool($v) ? ($v ? '<span class="chip ok">yes</span>' : '<span class="chip bad">no</span>') : _h((string)$v);
        echo '<tr><td class="k"' . ($narrow_key ? ' style="width:200px"' : '') . '>' . _h($k) . '</td><td class="v">' . $vs . '</td></tr>';
    }
    echo '</table>';
}

function _section($title, $count = null, $status = 'ok', $id = null) {
    $anchor = $id ?: strtolower(preg_replace('/[^a-z0-9]+/i', '-', (string)$title));
    echo '<section id="' . _h($anchor) . '">';
    echo '<div class="card status-' . _h($status) . '">';
    echo '  <div class="card-head">';
    echo '    <h2>' . _h($title);
    if ($count !== null) echo ' <span class="count">' . _h((string)$count) . '</span>';
    echo '</h2>';
    echo '    <button class="card-copybtn" onclick="copySection(this)">copy</button>';
    echo '  </div>';
    echo '  <div class="card-body">';
}
function _endsection() { echo '</div></div></section>'; }
?>

<!-- Row 1: PHP + Reseller sanity -->
<div class="grid2">
    <?php
    _section('PHP', null, 'ok');
    _kvtable($php);
    _endsection();

    // Reseller sanity — red if any variant is wrong
    $reseller_status = ($reseller_check['apksec_variant'] === 'reseller'
                       && strpos((string)$reseller_check['crypto_has_tail'], 'YES') !== 0)
                     ? 'ok' : 'bad';
    _section('Reseller sanity', null, $reseller_status);
    _kvtable($reseller_check);
    _endsection();
    ?>
</div>

<!-- Extensions — full width -->
<?php
$ext_status_kind = $missing_req_ext > 0 ? 'bad' : ($missing_opt_ext > 0 ? 'warn' : 'ok');
$ext_count_label = $missing_req_ext > 0
    ? $missing_req_ext . ' missing'
    : ($missing_opt_ext > 0
        ? count($ext_status) - $missing_opt_ext . ' loaded · ' . $missing_opt_ext . ' optional missing'
        : count($ext_status) . ' loaded');
_section('Extensions', $ext_count_label, $ext_status_kind);
?>
<table><tr><th>extension</th><th>state</th></tr>
<?php foreach ($ext_status as $e => $info):
    $loaded = $info['loaded'];
    $req    = $info['required'];
?>
<tr><td><?php echo _h($e); ?><?php if (!$req): ?> <span class="small">(optional)</span><?php endif; ?></td>
    <td><?php
        if ($loaded) echo '<span class="chip ok">loaded</span>';
        elseif ($req) echo '<span class="chip bad">MISSING</span> <span class="small">install via cPanel → Select PHP Version</span>';
        else echo '<span class="chip warn">missing</span> <span class="small">panel has a fallback, safe to skip</span>';
    ?></td></tr>
<?php endforeach; ?>
</table>
<?php _endsection(); ?>

<!-- Row 2: ini + config -->
<div class="grid2">
    <?php _section('ini settings', null, 'ok'); _kvtable($ini); _endsection(); ?>
    <?php _section('config row', null, 'ok'); _kvtable($cfg); _endsection(); ?>
</div>

<!-- File permissions — full width -->
<?php
$badp = 0;
foreach ($paths as $p) { $r = _perm($p); if ($r[2] === 'NO') $badp++; }
_section('File permissions', $badp > 0 ? $badp . ' need chmod' : count($paths) . ' checked', $badp > 0 ? 'bad' : 'ok');
?>
<table><tr><th>path</th><th>mode</th><th>owner</th><th>writable</th><th>type</th></tr>
<?php foreach ($paths as $p):
    [$m, $og, $w, $t] = _perm($p);
    $wclass = $w === 'yes' ? 'chip ok' : ($w === '—' ? 'chip' : 'chip bad');
?>
<tr>
    <td><?php echo _h(str_replace($panel_root, '.', $p)); ?></td>
    <td><?php echo _h($m); ?></td>
    <td class="small"><?php echo _h($og); ?></td>
    <td><span class="<?php echo $wclass; ?>"><?php echo _h($w); ?></span></td>
    <td class="small"><?php echo _h($t); ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php _endsection(); ?>

<!-- panel.db counts — full width -->
<?php _section('panel.db table counts', null, 'ok'); ?>
<table><tr><th>table</th><th>rows</th></tr>
<?php foreach ($db_stats as $t => $c): ?>
<tr><td><?php echo _h($t); ?></td><td class="mono"><?php echo _h((string)$c); ?></td></tr>
<?php endforeach; ?>
</table>
<?php _endsection(); ?>

<!-- Request log — always visible with empty state -->
<?php _section('Recent request log', count($recent_req) . ' shown', 'ok'); ?>
<?php if ($recent_req): ?>
<table><tr><th>time</th><th>method</th><th>path</th><th>status</th><th>ip</th></tr>
<?php foreach ($recent_req as $r): ?>
<tr>
    <td class="small"><?php echo _h($r['t']); ?></td>
    <td><?php echo _h($r['method']); ?></td>
    <td class="mono"><?php echo _h($r['path']); ?></td>
    <td><span class="chip <?php echo ((int)$r['s'] >= 400 ? 'bad' : 'ok'); ?>"><?php echo _h((string)$r['s']); ?></span></td>
    <td class="small"><?php echo _h($r['ip']); ?></td>
</tr>
<?php endforeach; ?>
</table>
<?php else: ?>
<p class="empty">no requests logged since last clear · buyer probably hasn't opened the app yet</p>
<?php endif; ?>
<?php _endsection(); ?>

<!-- Error log — always visible, most prominent when populated -->
<?php _section('Recent panel error log', $errcount . ' entries', $errcount > 0 ? 'bad' : 'ok'); ?>
<?php if ($panel_errs): ?>
<?php foreach (array_reverse($panel_errs) as $e): ?>
<div class="errcard">
    <div class="ts"><?php echo _h($e['ts_iso'] ?? '?'); ?><?php if (!empty($e['level'])): ?> · <?php echo _h($e['level']); ?><?php endif; ?></div>
    <div class="msg"><?php echo _h($e['message'] ?? ''); ?></div>
    <?php if (!empty($e['file']) || !empty($e['url'])): ?>
    <div class="meta">
        <?php if (!empty($e['file'])): ?><?php echo _h($e['file'] . ':' . ($e['line'] ?? '')); ?><?php endif; ?>
        <?php if (!empty($e['url'])): ?>&nbsp;·&nbsp;<?php echo _h(($e['method'] ?? '') . ' ' . $e['url']); ?><?php endif; ?>
    </div>
    <?php endif; ?>
</div>
<?php endforeach; ?>
<?php else: ?>
<p class="empty ok">✓ no panel errors — clean since last clear</p>
<?php endif; ?>
<?php _endsection(); ?>

<p class="small" style="text-align:center;margin-top:32px">Generated <?php echo _h(date('c')); ?> · panel root <code><?php echo _h($panel_root); ?></code></p>
</div>

<script>
const HERE_URL = <?php echo json_encode($here_url . '?key=' . $key); ?>;
const TXT_URL  = <?php echo json_encode($txt_url); ?>;

async function copyReport(btn) {
    try {
        const r = await fetch(TXT_URL, { credentials: 'same-origin' });
        const text = await r.text();
        await navigator.clipboard.writeText(text);
        const orig = btn.textContent;
        btn.textContent = 'Copied — paste it in chat';
        setTimeout(() => btn.textContent = orig, 2500);
    } catch (e) {
        alert('Copy failed: ' + e.message + '\nUse "Download .txt" or "Open plain text" instead.');
    }
}

async function copySection(btn) {
    const section = btn.closest('section');
    if (!section) return;
    const text = section.innerText.replace(/[ \t]+/g, ' ').replace(/\n{3,}/g, '\n\n');
    try {
        await navigator.clipboard.writeText(text);
        const orig = btn.textContent;
        btn.textContent = 'copied';
        setTimeout(() => btn.textContent = orig, 1600);
    } catch (e) { alert('Copy failed: ' + e.message); }
}

async function clearLog(action, btn) {
    const label = { clear_errors: 'panel error log', clear_request_log: 'request log', clear_all: 'both logs' }[action];
    if (!confirm('Clear ' + label + '? This cannot be undone.')) return;
    const orig = btn.textContent;
    btn.textContent = 'Working…'; btn.disabled = true;
    try {
        const fd = new FormData();
        fd.append('action', action);
        const r = await fetch(HERE_URL, { method: 'POST', credentials: 'same-origin', body: fd });
        const j = await r.json();
        if (!j.ok) throw new Error(j.error || 'failed');
        btn.textContent = 'Cleared';
        setTimeout(() => location.reload(), 700);
    } catch (e) {
        btn.textContent = orig; btn.disabled = false;
        alert('Clear failed: ' + e.message);
    }
}

// Auto-refresh — checkbox persists via sessionStorage
const auto = document.getElementById('autoref');
auto.checked = sessionStorage.getItem('debug_autoref') === '1';
let timer = null;
function armAutoRefresh() {
    if (timer) { clearInterval(timer); timer = null; }
    if (auto.checked) { timer = setInterval(() => location.reload(), 10000); }
    sessionStorage.setItem('debug_autoref', auto.checked ? '1' : '0');
}
auto.addEventListener('change', armAutoRefresh);
armAutoRefresh();
</script>

</body></html>
