<!DOCTYPE html>
<html lang="ru" data-bs-theme="light">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}TG Agent{% endblock %}</title>
    <link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>🔍</text></svg>">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" integrity="sha384-XGjxtQfXaH2tnPFa9x+ruJTuLE3Aa6LhHSWRr1XeTyhezb4abCG4ccI5AkVDxqC+" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/style.css">
    <script src="https://unpkg.com/htmx.org@2.0.4" integrity="sha384-HGfztofotfshcF7+8n44JQL2oJmowVChPTg48S+jvZoztPfvwD79OC/LTtG6dMp+" crossorigin="anonymous"></script>
    <script>
    (function(){var t=localStorage.getItem('theme')||(matchMedia('(prefers-color-scheme:dark)').matches?'dark':'light');document.documentElement.setAttribute('data-bs-theme',t)})();
    </script>
    {% block head_scripts %}{% endblock %}
</head>
<body class="d-flex flex-column min-vh-100">
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand fw-bold" href="/">TG Agent</a>
            <button class="navbar-toggler" type="button" data-bs-toggle="offcanvas" data-bs-target="#mainNav" aria-controls="mainNav" aria-label="Toggle navigation">
                <span class="navbar-toggler-icon"></span>
            </button>
            <div class="offcanvas offcanvas-end text-bg-dark" tabindex="-1" id="mainNav">
                <div class="offcanvas-header">
                    <h5 class="offcanvas-title">TG Agent</h5>
                    <button type="button" class="btn-close btn-close-white" data-bs-dismiss="offcanvas" aria-label="Close"></button>
                </div>
                <div class="offcanvas-body">
                <ul class="navbar-nav ms-auto">
                    {% if agent_available(request) %}<li class="nav-item"><a class="nav-link" href="/agent">Чат</a></li>{% endif %}
                    <li class="nav-item"><a class="nav-link" href="/search">Поиск</a></li>
                    <li class="nav-item"><a class="nav-link" href="/analytics">Аналитика</a></li>
                    <li class="nav-item"><a class="nav-link" href="/analytics/trends">Тренды</a></li>
                    <li class="nav-item"><a class="nav-link" href="/dashboard">Панель</a></li>
                    <li class="nav-item"><a class="nav-link" href="/channels">Каналы</a></li>
                    <li class="nav-item"><a class="nav-link" href="/channels/filter/manage">Очистка</a></li>
                    <li class="nav-item"><a class="nav-link" href="/search-queries">Запросы</a></li>
                    <li class="nav-item"><a class="nav-link" href="/pipelines">Пайплайны</a></li>
                    <li class="nav-item"><a class="nav-link" href="/images">Картинки</a></li>
                    <li class="nav-item"><a class="nav-link" href="/calendar">Календарь</a></li>
                    <li class="nav-item"><a class="nav-link" href="/my-telegram">Мой Телеграм</a></li>
                    <li class="nav-item"><a class="nav-link" href="/scheduler">Планировщик</a></li>
                    <li class="nav-item"><a class="nav-link" href="/debug">Дебаг</a></li>
                    <li class="nav-item"><a class="nav-link" href="/settings">Настройки</a></li>
                    <li class="nav-item"><a class="nav-link" href="/logout">Выйти</a></li>
                    <li class="nav-item ms-lg-2 mt-2 mt-lg-0">
                        <button class="btn btn-sm btn-outline-light" id="theme-toggle" type="button" title="Переключить тему"></button>
                    </li>
                </ul>
                </div>
            </div>
        </div>
    </nav>
    <div id="flash-container" class="container mt-3"></div>
    <main class="container py-3 flex-grow-1">
        {% block content %}{% endblock %}
    </main>
    <footer class="text-center text-muted py-3 mt-auto">
        <small>TG Agent v{{ app_version }}</small>
    </footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
<script>
(function() {
    var msgs = {
        channel_added: "Канал добавлен.",
        channels_added: "Каналы добавлены.",
        channel_toggled: "Статус канала изменён.",
        channel_deleted: "Канал удалён.",
        account_connected: "Аккаунт подключён.",
        credentials_saved: "API-данные сохранены.",
        filters_saved: "Фильтры сохранены.",
        scheduler_saved: "Настройки планировщика сохранены.",
        account_toggled: "Статус аккаунта изменён.",
        account_deleted: "Аккаунт удалён.",
        notification_account_saved: "Аккаунт для уведомлений сохранён.",
        notification_bot_created: "Notification bot создан.",
        notification_bot_deleted: "Notification bot удалён.",
        agent_saved: "Настройки AI Agent сохранены.",
        scheduler_started: "Планировщик запущен.",
        scheduler_stopped: "Планировщик остановлен.",
        collect_started: "Сбор сообщений запущен в фоне.",
        collect_queued: "Задача добавлена в очередь. Выполнится после текущего сбора.",
        collect_all_queued: 'Задачи на загрузку добавлены. Откройте <a href="/scheduler">планировщик</a>, чтобы следить за прогрессом.',
        collect_all_noop: 'Новых задач не добавлено. Откройте <a href="/scheduler">планировщик</a>, чтобы проверить текущую очередь.',
        collect_all_empty: "Нет активных каналов для загрузки.",
        test_notification_sent: "Тестовое уведомление отправлено.",
        test_notification_failed: "Не удалось отправить уведомление.",
        task_cancelled: "Задача отменена.",
        pending_collect_tasks_deleted: "Запланированные задачи на загрузку удалены из базы.",
        pending_collect_tasks_empty: "Запланированных задач на загрузку нет.",
        already_running: "Сбор уже запущен.",
        triggered: "Сбор запущен вручную.",
        filter_toggled: "Фильтр канала изменён.",
        filter_applied: "Фильтры применены.",
        filter_reset: "Фильтры сброшены.",
        channel_not_found: "Канал не найден.",
        stats_collection_started: "Сбор статистики запущен в фоне.",
        stats_collection_queued: "Сбор статистики поставлен в очередь.",
        search_triggered: "Поиск запущен.",
        no_accounts: "Для начала работы добавьте Telegram-аккаунт.",
         sq_added: "Поисковый запрос добавлен.",
         sq_deleted: "Поисковый запрос удалён.",
         sq_toggled: "Статус поискового запроса изменён.",
         sq_run: "Поисковый запрос выполнен.",
         sq_edited: "Поисковый запрос обновлён.",
         pipeline_added: "Pipeline добавлен.",
         pipeline_deleted: "Pipeline удалён.",
         pipeline_toggled: "Статус pipeline изменён.",
         pipeline_edited: "Pipeline обновлён.",
         pipeline_run_enqueued: "Запуск pipeline поставлен в очередь.",
         pipeline_published: "Pipeline опубликован.",
         notification_test_sent: "Тестовое уведомление отправлено.",
        photo_sent: "Фото отправлены.",
        photo_scheduled: "Фото поставлены в отложенную отправку Telegram.",
        photo_batch_created: "Batch photo tasks созданы.",
        photo_auto_created: "Авто-загрузка создана.",
        photo_run_due_ok: "Due photo tasks обработаны.",
        photo_item_cancelled: "Photo task отменён.",
        photo_auto_toggled: "Статус auto job изменён.",
        photo_auto_deleted: "Auto job удалён.",
        deleted_filtered: "Каналы полностью удалены из базы.",
        purged_selected: "Сообщения выбранных каналов очищены.",
        purged_all_filtered: "Сообщения всех отфильтрованных каналов очищены.",
        precheck_done: "Предпроверка подписчиков выполнена.",
        pipeline_added: "Пайплайн добавлен.",
        pipeline_edited: "Пайплайн обновлён.",
        pipeline_toggled: "Статус пайплайна изменён.",
        pipeline_deleted: "Пайплайн удалён.",
        semantic_saved: "Настройки semantic search сохранены.",
        semantic_indexed: "Индексация semantic search завершена."
    };
    var errors = {
        resolve: "Не удалось найти канал. Проверьте username или ссылку.",
        no_client: "Нет подключённых аккаунтов для поиска канала.",
        collecting: "Сбор уже выполняется. Дождитесь завершения.",
        shutting_down: "Приложение завершает работу.",
        bot_not_configured: "Бот не подключён. Настройте уведомления в Настройках.",
        stats_running: "Сбор статистики уже выполняется.",
        channel_filtered: "Канал помечен как бесполезный и исключён из сбора.",
        filter_snapshot_required: "Сначала выполните анализ фильтров и примените его из отчёта.",
        invalid_value: "Указано некорректное значение.",
        invalid_api_id: "API ID должен состоять только из цифр.",
        agent_dev_mode_required: "Для управления deepagents providers включите режим разработчика.",
        agent_prompt_template_invalid: "Шаблон промпта содержит неподдерживаемые или некорректные переменные.",
        notification_account_invalid: "Выбран неизвестный аккаунт уведомлений.",
        agent_provider_secret_required: "Для управления deepagents providers требуется SESSION_ENCRYPTION_KEY.",
        agent_provider_invalid: "Выбран неизвестный провайдер deepagents.",
        notification_account_unavailable: "Выбранный аккаунт уведомлений недоступен.",
        notification_bot_missing: "Для выбранного аккаунта notification bot не найден.",
        notification_action_failed: "Не удалось выполнить действие с notification bot.",
        no_filtered_channels: "Нет отфильтрованных каналов для удаления.",
        dev_mode_required_for_hard_delete: "Для полного удаления каналов включите режим разработчика в настройках.",
        notification_test_failed: "Не удалось отправить тестовое уведомление.",
        invalid_account: "Выбран неизвестный аккаунт.",
        not_found: "Объект не найден.",
        photo_send_failed: "Не удалось отправить фото.",
        photo_target_required: "Сначала выберите канал, чат или личный диалог.",
        photo_target_invalid: "Выбранная цель недоступна. Выберите её заново.",
        photo_schedule_failed: "Не удалось создать отложенную отправку фото.",
         photo_batch_failed: "Не удалось создать batch photo tasks.",
         photo_auto_failed: "Не удалось создать или изменить auto job.",
         photo_run_due_failed: "Не удалось выполнить due photo tasks.",
         semantic_invalid_value: "Некорректные настройки semantic search.",
         semantic_unavailable: "Текущий semantic backend недоступен в этой среде.",
         photo_item_cancel_failed: "Не удалось отменить photo task.",
         pipeline_invalid: "Параметры pipeline некорректны или dialog cache не заполнен.",
         pipeline_run_failed: "Не удалось поставить запуск pipeline в очередь."
     };
    var params = new URLSearchParams(window.location.search);
    var code = params.get("msg");
    var errCode = params.get("error");
    // Expose flash codes globally so deferred scripts (e.g. settings.js) can read them
    if (code) window.__flashMsg = code;
    if (errCode) window.__flashError = errCode;
    var container = document.getElementById("flash-container");
    if (code && msgs[code]) {
        container.innerHTML = '<div class="alert alert-success" role="alert">' + msgs[code] + '</div>';
    }
    if (errCode) {
        var errDiv = document.createElement("div");
        errDiv.className = "alert alert-danger";
        errDiv.setAttribute("role", "alert");
        if (errors[errCode]) {
            errDiv.innerHTML = errors[errCode];
        } else {
            errDiv.textContent = errCode;
        }
        container.appendChild(errDiv);
    }
    if (code || errCode) {
        params.delete("msg");
        params.delete("error");
        var qs = params.toString();
        var url = window.location.pathname + (qs ? "?" + qs : "") + window.location.hash;
        window.history.replaceState(null, "", url);
        setTimeout(function() { container.innerHTML = ""; }, 5000);
    }
    // Theme toggle
    var themeBtn = document.getElementById("theme-toggle");
    function applyTheme(t) {
        document.documentElement.setAttribute("data-bs-theme", t);
        themeBtn.textContent = t === "dark" ? "\u2600\uFE0F" : "\uD83C\uDF19";
        themeBtn.title = t === "dark" ? "Светлая тема" : "Тёмная тема";
    }
    var currentTheme = document.documentElement.getAttribute("data-bs-theme") || "light";
    applyTheme(currentTheme);
    themeBtn.addEventListener("click", function() {
        var next = document.documentElement.getAttribute("data-bs-theme") === "dark" ? "light" : "dark";
        localStorage.setItem("theme", next);
        applyTheme(next);
    });
    // Active nav highlight
    var path = window.location.pathname;
    var links = document.querySelectorAll("#mainNav .nav-link");
    function isActiveLink(path, href) {
        if (href === "/") {
            return path === "/";
        }
        if (path === href) {
            return true;
        }
        return path.startsWith(href + "/");
    }
    links.forEach(function(a) {
        var href = a.getAttribute("href");
        if (isActiveLink(path, href)) {
            a.classList.add("active");
        }
    });
    // Unified busy-button handler: add data-busy to any <form> for auto spinner
    document.addEventListener('submit', function(e) {
        if (e.defaultPrevented) return;
        var form = e.target;
        if (!form.hasAttribute('data-busy')) return;
        var btn = e.submitter || form.querySelector('button[type="submit"]');
        if (!btn || btn.disabled) return;
        btn.disabled = true;
        var label = btn.getAttribute('data-busy-label') || btn.textContent.trim();
        btn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> ' + label;
    });
    // Local date conversion: replace .local-dt[data-utc] spans with browser-local time.
    // Initial textContent is a server-side UTC fallback; JS replaces it once the page loads.
    function convertLocalDates(root) {
        (root || document).querySelectorAll('.local-dt[data-utc]').forEach(function(el) {
            var d = new Date(el.dataset.utc);
            if (isNaN(d)) return;
            var fmt = el.dataset.fmt || 'datetime';
            if (fmt === 'time') {
                el.textContent = d.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', second: '2-digit'});
            } else if (fmt === 'date') {
                el.textContent = d.toLocaleDateString();
            } else {
                el.textContent = d.toLocaleString([], {year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit'});
            }
        });
    }
    document.addEventListener('DOMContentLoaded', function() { convertLocalDates(); });
    document.body.addEventListener('htmx:afterSwap', function(e) { convertLocalDates(e.detail.target); });
    // Reusable spinner helper for fetch-based (non-form) buttons
    window.setAsyncButtonBusy = function(button, busy, busyLabel) {
        if (!button) return;
        if (!button.dataset.defaultLabel) {
            button.dataset.defaultLabel = button.innerHTML;
        }
        if (busy) {
            button.disabled = true;
            button.innerHTML =
                '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>' +
                (busyLabel || 'Выполняется...');
            return;
        }
        button.disabled = false;
        button.innerHTML = button.dataset.defaultLabel;
    };
})();
</script>
</body>
</html>
