<?php
// =============================================================
// DASHBOARD/LIB/LAYOUT — общий каркас страниц (Ink & Amber)
// =============================================================
// Топбар (бренд/назад, заголовок, статус бота), плавающий док
// внизу на мобайле → вертикальный рейл слева на десктопе (CSS),
// bottom-sheet «Ещё» с полной навигацией. Дизайн-система —
// assets/app.css + assets/app.js. Каждая страница вызывает
// layout_header([...]) до контента и layout_footer() после.

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

// ---------------------------------------------------------------
// Общие форматтеры значений (используются всеми страницами)
// ---------------------------------------------------------------

if (!function_exists('fmt_usd')) {
    function fmt_usd($val): string
    {
        if ($val === null) return '—';
        $sign = $val >= 0 ? '+' : '';
        return $sign . number_format($val, 2) . ' $';
    }
}

if (!function_exists('fmt_pct')) {
    function fmt_pct($val): string
    {
        if ($val === null) return '—';
        $sign = $val >= 0 ? '+' : '';
        return $sign . number_format($val, 2) . '%';
    }
}

if (!function_exists('pnl_class')) {
    function pnl_class($val): string
    {
        if ($val === null) return '';
        return $val >= 0 ? 'pos' : 'neg';
    }
}

// ---------------------------------------------------------------
// Иконки (inline SVG, stroke — стиль Ink & Amber)
// ---------------------------------------------------------------

function layout_icon(string $name): string
{
    static $icons = [
        'dashboard' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M3 11.5 12 4l9 7.5M5.5 10v9h13v-9"/></svg>',
        'traders' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="9" cy="8" r="3.4"/><path d="M3.5 19c.6-3 2.9-4.6 5.5-4.6s4.9 1.6 5.5 4.6M15.5 5.2a3.4 3.4 0 0 1 0 5.6M17.5 14.6c1.7.7 2.7 2.2 3 4.4"/></svg>',
        'pools' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="8" cy="9" r="4"/><circle cx="16" cy="15" r="4"/></svg>',
        'positions' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 17l4.5-5 3.5 3 4-6 4 4.5"/><path d="M4 21h16"/></svg>',
        'journal' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="5" y="3.5" width="14" height="17" rx="2.5"/><path d="M9 8.5h6M9 12h6M9 15.5h3.5"/></svg>',
        'analytics' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M5 20V10M12 20V4M19 20v-7"/></svg>',
        'bulk' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="7" height="7" rx="2"/><rect x="13" y="4" width="7" height="7" rx="2"/><rect x="4" y="13" width="7" height="7" rx="2"/><path d="M16.5 13.5v6M13.5 16.5h6"/></svg>',
        'settings' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M12 3v2.5M12 18.5V21M21 12h-2.5M5.5 12H3M18.4 5.6l-1.8 1.8M7.4 16.6l-1.8 1.8M18.4 18.4l-1.8-1.8M7.4 7.4 5.6 5.6"/></svg>',
        'logout' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4h-8v16h8M10 12h11M18 8.5 21.5 12 18 15.5"/></svg>',
        'more' => '<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="5" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="19" cy="12" r="2"/></svg>',
        'back' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.5 5 8 12l6.5 7"/></svg>',
        'search' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><circle cx="11" cy="11" r="6.5"/><path d="m16 16 4.5 4.5"/></svg>',
        'plus' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M12 5v14M5 12h14"/></svg>',
        'filter' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M4 6h16M7 12h10M10 18h4"/></svg>',
        'download' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 4v11M7.5 11.5 12 16l4.5-4.5M5 20h14"/></svg>',
        'burger' => '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="17" x2="20" y2="17"/></svg>',
    ];
    return $icons[$name] ?? '';
}

// ---------------------------------------------------------------
// Счётчики для бейджей навигации
// ---------------------------------------------------------------

/**
 * Дешёвые счётчики для бейджей навигации: трейдеры/пулы — из JSON-конфигов,
 * позиции (открытые + watching) — двумя COUNT-запросами к SQLite.
 * При любой ошибке — нули (бейдж просто не показывается).
 */
function layout_nav_counts(): array
{
    static $cache = null;
    if ($cache !== null) {
        return $cache;
    }
    $counts = ['traders' => 0, 'pools' => 0, 'positions' => 0];

    try {
        $cfg = load_wallets_config();
        $counts['traders'] = count($cfg['wallets'] ?? []);
    } catch (Throwable $e) { /* бейдж не критичен */ }

    try {
        if (function_exists('load_pools_config')) {
            $pd = load_pools_config();
            $counts['pools'] = count($pd['pools'] ?? []);
        } elseif (is_file(pools_config_path())) {
            $pd = json_decode((string) file_get_contents(pools_config_path()), true);
            $counts['pools'] = count($pd['pools'] ?? []);
        }
    } catch (Throwable $e) { /* бейдж не критичен */ }

    try {
        require_once __DIR__ . '/../../storage.php';
        $db = storage_db();
        $open  = (int) $db->query("SELECT COUNT(*) FROM open_positions WHERE entity_type = 'wallet'")->fetchColumn();
        $watch = (int) $db->query("SELECT COUNT(*) FROM watching WHERE entity_type = 'wallet'")->fetchColumn();
        $counts['positions'] = $open + $watch;
    } catch (Throwable $e) { /* бейдж не критичен */ }

    return $cache = $counts;
}

/**
 * Fallback-путь к pools.json, если pool_tracker.php не подключён.
 */
function pools_config_path(): string
{
    if (function_exists('private_state_path')) {
        return private_state_path('pools.json');
    }
    return STATE_DIR . '/pools.json';
}

// ---------------------------------------------------------------
// Статус бота для топбара (лёгкая версия check_cron_health)
// ---------------------------------------------------------------

/**
 * Состояние cron/бота по heartbeat-отметкам. Логика идентична
 * check_cron_health() из data.php, но без тяжёлых include —
 * топбар нужен каждой странице.
 * Возвращает ['class' => ok|warn|fail, 'text' => ..., 'title' => ...]
 */
function layout_bot_status(): array
{
    $lastRun = $secondsAgo = null;
    if (defined('HEARTBEAT_FILE') && file_exists(HEARTBEAT_FILE)) {
        $lastRun    = (int) trim((string) file_get_contents(HEARTBEAT_FILE));
        $secondsAgo = time() - $lastRun;
    }
    $lastTick = $tickAgo = $cronOk = null;
    if (defined('CRON_TICK_FILE') && file_exists(CRON_TICK_FILE)) {
        $lastTick = (int) trim((string) file_get_contents(CRON_TICK_FILE));
        $tickAgo  = time() - $lastTick;
        $cronOk   = $tickAgo <= 180;
    }
    $heartbeatOk = $secondsAgo !== null && $secondsAgo <= 420;
    $cronDead    = $cronOk === false || ($cronOk === null && !$heartbeatOk);

    if ($lastRun === null && $lastTick === null) {
        return ['class' => 'warn', 'text' => 'Cron ещё не запускался', 'title' => 'Отметок heartbeat пока нет'];
    }
    if ($cronDead) {
        $ago = $tickAgo ?? $secondsAgo;
        return [
            'class' => 'fail',
            'text'  => 'Бот не отвечает',
            'title' => 'Последний запуск ' . $ago . 'с назад (' . date('Y-m-d H:i:s', $lastTick ?? $lastRun) . ')',
        ];
    }
    if (!$heartbeatOk) {
        return [
            'class' => 'warn',
            'text'  => 'Циклы не завершаются',
            'title' => 'Cron жив, но бот не завершает циклы — похоже на проблемы с сетью/API, см. logs/bot.log',
        ];
    }
    return [
        'class' => 'ok',
        'text'  => 'бот работает · ' . $secondsAgo . 'с',
        'title' => 'Последний завершённый цикл: ' . date('Y-m-d H:i:s', $lastRun),
    ];
}

// ---------------------------------------------------------------
// Карта навигации
// ---------------------------------------------------------------

function layout_nav_map(array $counts): array
{
    return [
        ['group' => 'Обзор', 'items' => [
            ['id' => 'dashboard', 'label' => 'Обзор', 'href' => 'index.php', 'icon' => 'dashboard'],
            ['id' => 'analytics', 'label' => 'Аналитика', 'href' => 'analytics.php', 'icon' => 'analytics'],
        ]],
        ['group' => 'Торговля', 'items' => [
            ['id' => 'positions', 'label' => 'Позиции', 'href' => 'positions.php', 'icon' => 'positions', 'badge' => $counts['positions'] ?: null],
            ['id' => 'journal', 'label' => 'Журнал', 'href' => 'journal.php', 'icon' => 'journal'],
        ]],
        ['group' => 'Копирование', 'items' => [
            ['id' => 'traders', 'label' => 'Трейдеры', 'href' => 'traders.php', 'icon' => 'traders', 'badge' => $counts['traders'] ?: null],
            ['id' => 'pools', 'label' => 'Пулы', 'href' => 'pools.php', 'icon' => 'pools', 'badge' => $counts['pools'] ?: null],
            ['id' => 'bulk', 'label' => 'Массовые операции', 'href' => 'bulk.php', 'icon' => 'bulk'],
        ]],
        ['group' => 'Система', 'items' => [
            ['id' => 'settings', 'label' => 'Настройки', 'href' => 'settings.php', 'icon' => 'settings'],
            ['id' => 'logout', 'label' => 'Выйти', 'href' => 'index.php?logout=1', 'icon' => 'logout'],
        ]],
    ];
}

/**
 * Пункты плавающего дока. Первые 4 — мобильный док; extra видны
 * только в десктопном рейле (мобайл открывает их через «Ещё»).
 */
function layout_dock_items(array $counts): array
{
    return [
        'main' => [
            ['id' => 'dashboard', 'label' => 'Обзор', 'href' => 'index.php', 'icon' => 'dashboard'],
            ['id' => 'traders', 'label' => 'Трейдеры', 'href' => 'traders.php', 'icon' => 'traders'],
            ['id' => 'positions', 'label' => 'Позиции', 'href' => 'positions.php', 'icon' => 'positions', 'badge' => $counts['positions'] ?: null],
            ['id' => 'journal', 'label' => 'Журнал', 'href' => 'journal.php', 'icon' => 'journal'],
        ],
        'extra' => [
            ['id' => 'analytics', 'label' => 'Аналитика', 'href' => 'analytics.php', 'icon' => 'analytics'],
            ['id' => 'pools', 'label' => 'Пулы', 'href' => 'pools.php', 'icon' => 'pools', 'badge' => $counts['pools'] ?: null],
            ['id' => 'bulk', 'label' => 'Массовые операции', 'href' => 'bulk.php', 'icon' => 'bulk'],
            ['id' => 'settings', 'label' => 'Настройки', 'href' => 'settings.php', 'icon' => 'settings'],
            ['id' => 'logout', 'label' => 'Выйти', 'href' => 'index.php?logout=1', 'icon' => 'logout', 'cls' => 'd-logout'],
        ],
    ];
}

// ---------------------------------------------------------------
// Header / Footer
// ---------------------------------------------------------------

/**
 * Печатает <!DOCTYPE>…<head>…, топбар и открывает .app/<main>.
 *
 * $opts:
 *   'title' => string            заголовок топбара (и <title>)
 *   'page'  => string            id активного пункта навигации
 *   'back'  => string|null       href кнопки «назад»
 *   'crumb' => ['label','href']  хлебная крошка (подзаголовок под títle)
 *   'sub'   => string            произвольный подзаголовок (если нет crumb)
 *   'head'  => string            доп. HTML в <head> (скрипты/стили страницы)
 */
/**
 * Версия ассета для обхода кэша браузера — время изменения файла.
 *
 * Без неё браузер бесконечно держит старый app.css: при выкладке новой версии
 * страница приезжает с новой разметкой, но со СТАРЫМИ стилями, и выглядит как
 * нестилизованная простыня (крупный жирный текст, поля без карточек, слипшиеся
 * блоки). Симптом легко принять за «сломался дизайн», хотя сломан только кэш.
 * filemtime дёшев (stat, кэшируется ОС) и меняется ровно при выкладке.
 */
function asset_version(string $relativePath): string
{
    $full = __DIR__ . '/../' . $relativePath;
    $mtime = @filemtime($full);
    return $mtime !== false ? (string) $mtime : '0';
}

function layout_header(array $opts): void
{
    $title  = $opts['title'] ?? 'Hyper Analytics';
    $page   = $opts['page'] ?? '';
    $status = layout_bot_status();
    ?>
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#0B0D12">
<title><?= htmlspecialchars($title) ?> — Hyper Analytics</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Archivo:wght@500;600;700;800&family=IBM+Plex+Mono:wght@400;500;600&family=Inter:wght@400;500;600&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/app.css?v=<?= asset_version('assets/app.css') ?>">
<script src="assets/app.js?v=<?= asset_version('assets/app.js') ?>"></script>
<?= $opts['head'] ?? '' ?>
</head>
<body data-page="<?= htmlspecialchars($page) ?>">
<div class="app">
    <header class="topbar">
        <?php if (!empty($opts['back'])): ?>
        <a class="icon-btn" href="<?= htmlspecialchars($opts['back']) ?>" title="Назад" aria-label="Назад"><?= layout_icon('back') ?></a>
        <?php else: ?>
        <div class="brand-dot">H</div>
        <?php endif; ?>
        <h1 class="tb-title"><?= htmlspecialchars($title) ?>
            <?php if (!empty($opts['crumb'])): ?>
            <span class="tb-sub"><a href="<?= htmlspecialchars($opts['crumb']['href']) ?>">← <?= htmlspecialchars($opts['crumb']['label']) ?></a></span>
            <?php elseif (!empty($opts['sub'])): ?>
            <span class="tb-sub"><?= htmlspecialchars($opts['sub']) ?></span>
            <?php endif; ?>
        </h1>
        <a class="tb-status <?= $status['class'] ?>" href="settings.php#system" title="<?= htmlspecialchars($status['title']) ?>"><span class="dot"></span><span class="lbl"><?= htmlspecialchars($status['text']) ?></span></a>
    </header>
    <main class="content">
    <?php
}

/**
 * Закрывает <main>/.app, печатает плавающий док (мобайл) /
 * рейл (десктоп), bottom-sheet «Ещё» и контейнер тостов.
 */
function layout_footer(): void
{
    $page   = '';
    $counts = layout_nav_counts();
    $nav    = layout_nav_map($counts);
    $dock   = layout_dock_items($counts);
    ?>
    </main>
</div><!-- /app -->
<nav class="dock">
    <div class="dock-brand"><div class="brand-dot">H</div><span>Hyper<br>Analytics</span></div>
    <?php foreach ($dock['main'] as $it): ?>
    <a class="d-main d-<?= $it['id'] ?>" href="<?= htmlspecialchars($it['href']) ?>"><?= layout_icon($it['icon']) ?><span><?= htmlspecialchars($it['label']) ?></span><?php if (!empty($it['badge'])): ?><span class="nav-badge"><?= (int) $it['badge'] ?></span><?php endif; ?></a>
    <?php endforeach; ?>
    <a class="d-more" href="javascript:void(0)" onclick="UI.openSheet()"><?= layout_icon('more') ?><span>Ещё</span></a>
    <?php foreach ($dock['extra'] as $it): ?>
    <a class="d-extra d-<?= $it['id'] ?> <?= $it['cls'] ?? '' ?>" href="<?= htmlspecialchars($it['href']) ?>"><?= layout_icon($it['icon']) ?><span><?= htmlspecialchars($it['label']) ?></span><?php if (!empty($it['badge'])): ?><span class="nav-badge"><?= (int) $it['badge'] ?></span><?php endif; ?></a>
    <?php endforeach; ?>
</nav>
<div class="sheet-backdrop" onclick="UI.closeSheet()"></div>
<div class="sheet">
    <div class="sheet-grip"></div>
    <?php foreach ($nav as $group): ?>
    <h3><?= htmlspecialchars($group['group']) ?></h3>
        <?php foreach ($group['items'] as $item): ?>
        <a class="sb-item" href="<?= htmlspecialchars($item['href']) ?>"><?= layout_icon($item['icon']) ?><span><?= htmlspecialchars($item['label']) ?></span><?php if (!empty($item['badge'])): ?><span class="sb-badge"><?= (int) $item['badge'] ?></span><?php endif; ?></a>
        <?php endforeach; ?>
    <?php endforeach; ?>
</div>
<div class="toast" id="toast"></div>
<script>
// Подсветка активного пункта дока/рейла
(function () {
    var page = document.body.dataset.page;
    document.querySelectorAll('.dock .d-' + page).forEach(function (el) { el.classList.add('on'); });
})();
</script>
</body>
</html>
    <?php
}

/**
 * Flash-сообщение результата POST-действия.
 */
function layout_flash(string $message, string $type): void
{
    if ($message === '') {
        return;
    }
    echo '<div class="message ' . htmlspecialchars($type ?: 'info') . '">' . htmlspecialchars($message) . '</div>';
}
