window._ap = window._ap || {};
class AccountPicker {
            constructor(config) {
                this.container = document.getElementById(config.containerId);
                this.mode = config.mode || 'multi';
                this.maxSelect = config.maxSelect || null;
                this.accentColor = config.accentColor || 'var(--primary)';
                this.showProjectColumn = config.showProjectColumn || false;
                this.perPage = config.perPage || 10;
                this.perPageOptions = config.perPageOptions || [10, 20, 50];
                this.existingIds = config.existingIds || new Set();
                this.existingLabel = config.existingLabel || 'В группе';
                this.customIsDisabled = config.customIsDisabled || null;
                this.customStatusHtml = config.customStatusHtml || null;
                this.onSelectionChange = config.onSelectionChange || (() => {});
                this.excludeIds = config.excludeIds || new Set();
                this.accounts = [];
                this.filtered = [];
                this.projects = [];
                this.selected = this.mode === 'multi' ? new Set() : null;
                this.selectedId = this.mode === 'single' ? null : undefined;
                this.page = 1;
                this.uid = 'ap_' + Math.random().toString(36).substr(2, 6);
                window._ap[this.uid] = this;
                this._buildHtml();
            }

            _buildHtml() {
                const u = this.uid;
                const ac = this.accentColor;
                const multiControls = this.mode === 'multi' ? `
                    <div style="margin-left:auto;display:flex;gap:6px;">
                        <button onclick="window._ap['${u}'].selectAllFiltered()" style="padding:6px 12px;border-radius:6px;border:1px solid ${ac};background:transparent;color:${ac};cursor:pointer;font-size:0.75rem;font-weight:500;">Выбрать все</button>
                        <button onclick="window._ap['${u}'].clearSelection()" style="padding:6px 12px;border-radius:6px;border:1px solid var(--border);background:transparent;color:var(--text-secondary);cursor:pointer;font-size:0.75rem;">Сбросить</button>
                    </div>` : '';
                const selectAllCb = this.mode === 'multi'
                    ? `<input type="checkbox" id="${u}_selall" onchange="window._ap['${u}'].togglePageAll(this)" style="cursor:pointer;width:14px;height:14px;accent-color:${ac};" title="Выбрать на странице">`
                    : '';
                const projCol = this.showProjectColumn
                    ? '<div style="padding:6px 4px;width:80px;font-size:.72rem;color:var(--text-secondary);font-weight:600;">Проект</div>' : '';
                const bottomMulti = this.mode === 'multi' ? `
                    <button onclick="window._ap['${u}'].selectPage()" id="${u}_selbtn" style="padding:4px 10px;border-radius:5px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);cursor:pointer;font-size:0.75rem;">Выбрать ${this.perPage}</button>
                    <button onclick="window._ap['${u}'].selectAllFiltered()" id="${u}_selallbtn" style="padding:4px 10px;border-radius:5px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);cursor:pointer;font-size:0.75rem;">Выбрать все (0)</button>
                    <button onclick="window._ap['${u}'].clearSelection()" style="padding:4px 10px;border-radius:5px;border:1px solid var(--border);background:var(--bg-card);color:var(--text-secondary);cursor:pointer;font-size:0.75rem;">Сбросить</button>
                ` : '';
                const ppOpts = this.perPageOptions.map(n => `<option value="${n}" ${n === this.perPage ? 'selected' : ''}>${n}</option>`).join('');

                this.container.innerHTML = `
                    <div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;flex-wrap:wrap;padding:8px 10px;background:var(--bg-card);border-radius:8px;">
                        <div style="position:relative;min-width:160px;flex:1;max-width:220px;">
                            <input type="text" id="${u}_search" placeholder="🔍 Поиск" oninput="window._ap['${u}'].filter()" style="width:100%;padding:6px 10px;border-radius:6px;border:1px solid var(--border);background:var(--input-bg);color:var(--text);font-size:0.8rem;box-sizing:border-box;">
                            <span id="${u}_count" style="position:absolute;right:8px;top:50%;transform:translateY(-50%);font-size:0.7rem;color:var(--text-secondary);"></span>
                        </div>
                        <select id="${u}_project" onchange="window._ap['${u}'].filter()" style="padding:6px 8px;border-radius:6px;border:1px solid var(--border);background:var(--input-bg);color:var(--text);font-size:0.75rem;min-width:120px;">
                            <option value="">Все проекты</option>
                            <option value="none">Без проекта</option>
                        </select>
                        <select id="${u}_day" onchange="window._ap['${u}'].filter()" style="padding:6px 8px;border-radius:6px;border:1px solid var(--border);background:var(--input-bg);color:var(--text);font-size:0.75rem;min-width:80px;">
                            <option value="">📅 День</option>
                            <option value="0">Не начат</option>
                            <option value="1">1</option><option value="2">2</option><option value="3">3</option>
                            <option value="4">4</option><option value="5">5</option><option value="6">6</option>
                            <option value="7">7</option><option value="8">8</option><option value="9">9</option>
                            <option value="10">10</option><option value="11">11</option><option value="12">12</option>
                            <option value="13">13</option><option value="14">14</option><option value="15">15</option>
                        </select>
                        <select id="${u}_status" onchange="window._ap['${u}'].filter()" style="padding:6px 8px;border-radius:6px;border:1px solid var(--border);background:var(--input-bg);color:var(--text);font-size:0.75rem;min-width:110px;">
                            <option value="">Все статусы</option>
                            <option value="active">✓ Активный</option>
                            <option value="resting">😴 Отлёжка</option>
                            <option value="spamblock">🤖 Спамблок</option>
                            <option value="frozen">🧊 Заморожен TG</option>
                            <option value="banned">🚫 Забанен TG</option>
                            <option value="dead">💀 Сессия битая</option>
                            <option value="session_revoked">🔑 Сессия сброшена</option>
                            <option value="deactivated">☠️ Аккаунт удалён</option>
                            <option value="no_session">📁 Без .session</option>
                            <option value="problems">⚠️ Все проблемные</option>
                        </select>
                        <select id="${u}_task" onchange="window._ap['${u}'].filter()" style="padding:6px 8px;border-radius:6px;border:1px solid var(--border);background:var(--input-bg);color:var(--text);font-size:0.75rem;min-width:100px;">
                            <option value="">Задачи</option>
                            <option value="free">✓ Свободные</option>
                            <option value="busy">⏳ В задачах</option>
                            <option value="inviting">📩 Инвайтинг</option>
                            <option value="warmup">🔥 Прогрев акк.</option>
                            <option value="group_warmer">🔥 Прогрев группы</option>
                            <option value="parsing">🔍 Парсинг</option>
                        </select>
                        <button onclick="window._ap['${u}'].resetFilters()" style="padding:6px 10px;border-radius:6px;border:1px solid var(--border);background:var(--bg-hover);color:var(--text-secondary);cursor:pointer;font-size:0.75rem;" title="Сбросить фильтры">✕</button>
                        ${multiControls}
                    </div>
                    <div style="flex:1;overflow:hidden;display:flex;flex-direction:column;min-height:0;">
                        <div style="background:var(--bg-card);border:1px solid var(--border);border-radius:8px;overflow:hidden;display:flex;flex-direction:column;height:100%;">
                            <div style="display:flex;align-items:center;border-bottom:1px solid var(--border);background:var(--bg-hover);flex-shrink:0;">
                                <div style="padding:6px 8px 6px 12px;width:36px;">${selectAllCb}</div>
                                <div style="padding:6px 4px;flex:1;font-size:.72rem;color:var(--text-secondary);font-weight:600;">Аккаунт</div>
                                <div style="padding:6px 4px;min-width:130px;max-width:200px;font-size:.72rem;color:var(--text-secondary);font-weight:600;">Статус</div>
                                <div style="padding:6px 4px;width:40px;font-size:.72rem;color:var(--text-secondary);font-weight:600;text-align:center;">День</div>
                                ${projCol}
                            </div>
                            <div id="${u}_body" style="flex:1;overflow-y:auto;"></div>
                        </div>
                    </div>
                    <div style="display:flex;justify-content:space-between;align-items:center;padding-top:10px;flex-wrap:wrap;gap:8px;">
                        <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
                            <span id="${u}_info" style="font-size:0.85rem;font-weight:500;color:var(--text-secondary);">${this.mode === 'multi' ? 'Выбрано: 0' : 'Выберите аккаунт'}</span>
                            ${bottomMulti}
                        </div>
                        <div style="display:flex;align-items:center;gap:6px;">
                            <select id="${u}_perpage" onchange="window._ap['${u}'].changePerPage()" style="padding:4px 8px;border-radius:6px;border:1px solid var(--border);background:var(--input-bg);color:var(--text);font-size:0.75rem;">${ppOpts}</select>
                            <div id="${u}_pages" style="display:flex;align-items:center;gap:3px;"></div>
                        </div>
                    </div>`;
            }

            setAccounts(accounts) {
                this.accounts = accounts;
                this.filtered = [...accounts];
                this.page = 1;
                this._updateSearchCount();
                this.renderTable();
                this.renderPagination();
                this._updateInfo();
            }

            setProjects(projects) {
                this.projects = projects;
                const sel = document.getElementById(this.uid + '_project');
                if (sel) sel.innerHTML = '<option value="">Все проекты</option><option value="none">Без проекта</option>' +
                    projects.map(p => `<option value="${p.id}">${escapeHtml(p.name)}</option>`).join('');
            }

            setSelection(ids) {
                if (this.mode === 'multi') {
                    this.selected = new Set(ids);
                } else {
                    this.selectedId = ids;
                }
                this.renderTable();
                this._updateInfo();
            }

            filter() {
                const search = (document.getElementById(this.uid + '_search')?.value || '').toLowerCase();
                const project = document.getElementById(this.uid + '_project')?.value || '';
                const day = document.getElementById(this.uid + '_day')?.value || '';
                const status = document.getElementById(this.uid + '_status')?.value || '';
                const task = document.getElementById(this.uid + '_task')?.value || '';
                const problemStatuses = ['dead', 'banned', 'session_revoked', 'deactivated', 'frozen', 'no_session', 'proxy_error'];
                this.filtered = this.accounts.filter(acc => {
                    if (this.excludeIds.has(acc.id)) return false;
                    if (search) {
                        const str = [acc.phone, acc.first_name, acc.last_name, acc.username].filter(Boolean).join(' ').toLowerCase();
                        if (!str.includes(search)) return false;
                    }
                    if (project === 'none' && acc.project_id) return false;
                    if (project && project !== 'none' && acc.project_id != project) return false;
                    if (day !== '') {
                        const accDay = acc.current_day || acc.warmup_day || 0;
                        if (parseInt(day) !== accDay) return false;
                    }
                    // Status filter (health)
                    if (status === 'active' && acc.status !== 'active') return false;
                    if (status === 'spamblock' && acc.status !== 'spamblock' && acc.spam_status !== 'limited') return false;
                    if (status === 'resting') {
                        const isResting = acc.resting_until && new Date(acc.resting_until) > new Date();
                        if (!isResting) return false;
                    }
                    if (status === 'frozen' && acc.status !== 'frozen') return false;
                    if (status === 'banned' && acc.status !== 'banned') return false;
                    if (status === 'dead' && acc.status !== 'dead') return false;
                    if (status === 'session_revoked' && acc.status !== 'session_revoked') return false;
                    if (status === 'deactivated' && acc.status !== 'deactivated') return false;
                    if (status === 'no_session' && acc.status !== 'no_session') return false;
                    if (status === 'problems' && !problemStatuses.includes(acc.status)) return false;
                    // Task filter (по привязке к задачам, не по активности)
                    const hasAnyTask = acc.in_warmup || acc.in_inviting || acc.in_group_warmer || acc.in_parsing;
                    if (task === 'free' && hasAnyTask) return false;
                    if (task === 'busy' && !hasAnyTask) return false;
                    if (task === 'inviting' && !acc.in_inviting) return false;
                    if (task === 'warmup' && !acc.in_warmup) return false;
                    if (task === 'group_warmer' && !acc.in_group_warmer) return false;
                    if (task === 'parsing' && !acc.in_parsing) return false;
                    return true;
                });
                this.page = 1;
                this._updateSearchCount();
                this.renderTable();
                this.renderPagination();
                this._updateInfo();
            }

            resetFilters() {
                const u = this.uid;
                const s = document.getElementById(u + '_search'); if (s) s.value = '';
                const p = document.getElementById(u + '_project'); if (p) p.value = '';
                const d = document.getElementById(u + '_day'); if (d) d.value = '';
                const st = document.getElementById(u + '_status'); if (st) st.value = '';
                const t = document.getElementById(u + '_task'); if (t) t.value = '';
                this.filter();
            }

            _isDisabled(acc) {
                if (this.existingIds.has(acc.id)) return true;
                if (acc.resting_until && new Date(acc.resting_until).getTime() > Date.now()) return true;
                if (acc.daily_invite_limit && acc.invites_today >= acc.daily_invite_limit) return true;
                // Universal busy/dead check — block accounts in any active task
                const _busyStatuses = ['warming', 'inviting', 'parsing', 'group_warming', 'monitoring'];
                const _deadStatuses = ['dead', 'banned', 'session_revoked', 'deactivated', 'frozen', 'no_session'];
                if (_busyStatuses.includes(acc.status) || _deadStatuses.includes(acc.status) || !acc.has_session) return true;
                if (acc.busy_in) return true;
                if (this.customIsDisabled) return this.customIsDisabled(acc);
                return false;
            }

            renderTable() {
                const body = document.getElementById(this.uid + '_body');
                if (!body) return;
                const start = (this.page - 1) * this.perPage;
                const pageAccounts = this.filtered.slice(start, start + this.perPage);
                if (!pageAccounts.length) {
                    body.innerHTML = '<div style="padding:30px;text-align:center;color:var(--text-secondary);">Нет аккаунтов</div>';
                    return;
                }
                const _actMap = {
                    warmup:{t:'Прогрев',s:'background:rgba(33,150,243,.15);color:#2196f3;',i:'🔥'},
                    warming:{t:'Прогрев',s:'background:rgba(33,150,243,.15);color:#2196f3;',i:'🔥'},
                    group_warmer:{t:'Группа',s:'background:rgba(255,152,0,.15);color:#ff9800;',i:'🔥'},
                    group_warming:{t:'Группа',s:'background:rgba(255,152,0,.15);color:#ff9800;',i:'🔥'},
                    parsing:{t:'Парсинг',s:'background:rgba(156,39,176,.15);color:#9c27b0;',i:'🔍'},
                    inviting:{t:'Инвайт',s:'background:rgba(0,188,212,.15);color:#00bcd4;',i:'📩'},
                    sending:{t:'Рассылка',s:'background:rgba(233,30,99,.15);color:#e91e63;',i:'📨'},
                    monitoring:{t:'Мониторинг',s:'background:rgba(76,175,80,.15);color:#4caf50;',i:'🔍'},
                };
                const _bStyle = 'display:inline-block;font-size:.55rem;padding:1px 5px;border-radius:3px;margin:1px 2px;white-space:nowrap;';
                const u = this.uid, ac = this.accentColor;
                body.innerHTML = pageAccounts.map(acc => {
                    const isExisting = this.existingIds.has(acc.id);
                    const isResting = acc.resting_until && new Date(acc.resting_until).getTime() > Date.now();
                    const limitExhausted = acc.daily_invite_limit && acc.invites_today >= acc.daily_invite_limit;
                    const isBusy = !!acc.busy_in || !!acc.busy_reason;
                    const isSpam = (acc.spam_status === 'limited' || acc.status === 'spamblock') && !(typeof isSpamBlockExpired === 'function' && isSpamBlockExpired(acc));
                    const isDisabled = this._isDisabled(acc);
                    const isSelected = this.mode === 'multi' ? this.selected.has(acc.id) : this.selectedId === acc.id;
                    const avatarLetter = (acc.first_name || acc.phone || '?')[0].toUpperCase();
                    const avatarUrl = acc.avatar_url ? `${API_URL}${acc.avatar_url}` : (acc.avatar_base64 ? `data:image/jpeg;base64,${acc.avatar_base64}` : null);
                    const isAuthAvatar = avatarUrl && avatarUrl.includes('/accounts/') && avatarUrl.includes('/avatar');
                    const avatar = avatarUrl
                        ? `<img ${isAuthAvatar ? `data-auth-src="${avatarUrl}" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"` : `src="${avatarUrl}"`} style="width:30px;height:30px;border-radius:50%;object-fit:cover;background:var(--bg-hover);${isDisabled ? 'opacity:0.4;' : ''}" loading="lazy" onerror="this.style.display='none';this.nextElementSibling.style.display='flex';"><div style="display:none;width:30px;height:30px;border-radius:50%;background:var(--bg-hover);align-items:center;justify-content:center;font-size:0.75rem;color:var(--text-secondary);">${avatarLetter}</div>`
                        : `<div style="width:30px;height:30px;border-radius:50%;background:${isDisabled ? 'var(--bg-hover)' : ac};display:flex;align-items:center;justify-content:center;color:${isDisabled ? 'var(--text-secondary)' : '#000'};font-weight:bold;font-size:11px;">${avatarLetter}</div>`;
                    // Build badges
                    let badges = '';
                    if (this.customStatusHtml) { badges = this.customStatusHtml(acc); }
                    else {
                        // Health badge
                        if (isExisting) {
                            badges += `<span style="${_bStyle}background:rgba(245,158,11,.2);color:#f59e0b;">✓ ${escapeHtml(this.existingLabel)}</span>`;
                        } else if (isSpam) {
                            const isPerm = acc.spam_until === 'permanent';
                            badges += `<span style="${_bStyle}${isPerm ? 'background:rgba(255,107,107,.2);color:#ff6b6b;' : 'background:rgba(255,152,0,.2);color:#ff9800;'}">🤖 ${isPerm ? 'Спам Пост.' : 'Спамблок'}</span>`;
                        } else if (acc.status === 'active' || isBusy) {
                            badges += `<span style="${_bStyle}background:rgba(245,158,11,.2);color:#f59e0b;">✓ Активный</span>`;
                        } else {
                            const _hm = {
                                dead:{t:'💀 Сессия битая',c:'#ff6b6b'},
                                banned:{t:'🚫 Забанен TG',c:'#ff6b6b'},
                                proxy_error:{t:'🌐 Ошибка прокси',c:'#ff9800'},
                                frozen:{t:'🧊 Заморожен',c:'#63b3ed'},
                                session_revoked:{t:'🔑 Сессия сброшена',c:'#ff9800'},
                                deactivated:{t:'☠️ Удалён',c:'#ff6b6b'},
                                no_session:{t:'📁 Без .session',c:'#9e9e9e'},
                                pending:{t:'? Ожидает',c:'#ffc107'},
                                checking:{t:'⏳ Проверка',c:'#2196f3'},
                            };
                            const h = _hm[acc.status];
                            if (h) badges += `<span style="${_bStyle}background:rgba(255,107,107,.15);color:${h.c};">${h.t}</span>`;
                            else badges += `<span style="${_bStyle}background:rgba(245,158,11,.2);color:#f59e0b;">✓</span>`;
                        }
                        // Activity badges (tasks)
                        if (acc.busy_in) {
                            const act = _actMap[acc.busy_in];
                            if (act) badges += `<span style="${_bStyle}${act.s}">${act.i} ${act.t}</span>`;
                        }
                        // Resting badge
                        if (isResting) {
                            const rd = new Date(acc.resting_until).toLocaleString('ru-RU',{hour:'2-digit',minute:'2-digit'});
                            badges += `<span style="${_bStyle}background:rgba(156,39,176,.2);color:#ab47bc;">😴 до ${rd}</span>`;
                        }
                        // Limit exhausted badge
                        if (limitExhausted) {
                            badges += `<span style="${_bStyle}background:rgba(255,107,107,.2);color:#ff6b6b;">🚫 Лимит</span>`;
                        }
                    }
                    const accDay = acc.current_day || acc.warmup_day || 0;
                    const dayText = accDay > 0 ? `${accDay}/15` : '-';
                    const selectedBg = isSelected ? `background:rgba(245,158,11,0.1);${this.mode === 'single' ? 'border-left:3px solid ' + ac + ';' : ''}` : '';
                    const clickFn = isDisabled ? '' : `window._ap['${u}'].toggleSelect(${acc.id})`;
                    const inputHtml = this.mode === 'multi'
                        ? `<input type="checkbox" ${isSelected ? 'checked' : ''} ${isDisabled ? 'disabled' : ''} onclick="event.stopPropagation();window._ap['${u}'].toggleSelect(${acc.id})" style="cursor:${isDisabled ? 'not-allowed' : 'pointer'};width:14px;height:14px;accent-color:${ac};">`
                        : `<input type="radio" name="${u}_radio" value="${acc.id}" ${isSelected ? 'checked' : ''} ${isDisabled ? 'disabled' : ''} style="cursor:${isDisabled ? 'not-allowed' : 'pointer'};width:14px;height:14px;accent-color:${ac};">`;
                    const projCol = this.showProjectColumn ? `<div style="padding:0 8px;width:80px;font-size:0.68rem;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(acc.project_name || '—')}</div>` : '';
                    const _busyLabels = {warming:'В прогреве',inviting:'В инвайтинге',parsing:'В парсинге',group_warming:'В прогреве группы',monitoring:'В мониторинге',warmup:'В прогреве',group_warmer:'В прогреве группы',broadcast:'В рассылке',sending:'В рассылке'};
                    let dTitle = '';
                    if (isExisting) dTitle = this.existingLabel;
                    else if (acc.busy_in && _busyLabels[acc.busy_in]) dTitle = _busyLabels[acc.busy_in];
                    else if (acc.status && _busyLabels[acc.status]) dTitle = _busyLabels[acc.status];
                    else if (isResting) dTitle = 'На отлёжке';
                    else if (limitExhausted) dTitle = 'Лимит исчерпан';
                    return `<div style="display:flex;align-items:center;border-bottom:1px solid var(--border);padding:6px 0;cursor:${isDisabled ? 'not-allowed' : 'pointer'};${isDisabled ? 'opacity:0.55;' : ''}${selectedBg}" ${clickFn ? `onclick="${clickFn}"` : ''} ${dTitle ? `title="${escapeHtml(dTitle)}"` : ''}>
                        <div style="padding:0 6px 0 12px;width:36px;">${inputHtml}</div>
                        <div style="flex:1;display:flex;align-items:center;gap:8px;padding:0 4px;min-width:0;">
                            ${avatar}
                            <div style="min-width:0;">
                                <div style="font-weight:500;font-size:0.82rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${escapeHtml(acc.first_name || '')} ${escapeHtml(acc.last_name || '')}</div>
                                <div style="font-size:0.72rem;color:var(--text-secondary);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${acc.username ? '@' + escapeHtml(acc.username) + ' • ' : ''}${escapeHtml(acc.phone)}</div>
                            </div>
                        </div>
                        <div style="min-width:130px;max-width:200px;padding:0 4px;display:flex;flex-wrap:wrap;align-items:center;">${badges}</div>
                        <div style="width:40px;padding:0 4px;font-size:0.78rem;text-align:center;flex-shrink:0;">${dayText}</div>
                        ${projCol}
                    </div>`;
                }).join('');
                if (this.mode === 'multi') {
                    const cb = document.getElementById(u + '_selall');
                    if (cb) {
                        const freeOnPage = pageAccounts.filter(a => !this._isDisabled(a));
                        cb.checked = freeOnPage.length > 0 && freeOnPage.every(a => this.selected.has(a.id));
                    }
                }
                // Загружаем аватарки с авторизацией
                loadAuthAvatars();
            }

            toggleSelect(id) {
                const acc = this.accounts.find(a => a.id === id);
                if (acc && this._isDisabled(acc)) return;
                if (this.mode === 'single') {
                    this.selectedId = this.selectedId === id ? null : id;
                } else {
                    if (this.selected.has(id)) { this.selected.delete(id); }
                    else {
                        if (this.maxSelect && this.selected.size >= this.maxSelect) return;
                        this.selected.add(id);
                    }
                }
                this.renderTable();
                this._updateInfo();
                this._notifyChange();
            }

            togglePageAll(checkbox) {
                const start = (this.page - 1) * this.perPage;
                this.filtered.slice(start, start + this.perPage).forEach(acc => {
                    if (this._isDisabled(acc)) return;
                    if (checkbox.checked) {
                        if (!this.maxSelect || this.selected.size < this.maxSelect) this.selected.add(acc.id);
                    } else { this.selected.delete(acc.id); }
                });
                this.renderTable();
                this._updateInfo();
                this._notifyChange();
            }

            selectPage() {
                const start = (this.page - 1) * this.perPage;
                this.filtered.slice(start, start + this.perPage).forEach(acc => {
                    if (!this._isDisabled(acc) && (!this.maxSelect || this.selected.size < this.maxSelect))
                        this.selected.add(acc.id);
                });
                this.renderTable(); this._updateInfo(); this._notifyChange();
            }

            selectAllFiltered() {
                this.filtered.forEach(acc => {
                    if (!this._isDisabled(acc) && (!this.maxSelect || this.selected.size < this.maxSelect))
                        this.selected.add(acc.id);
                });
                this.renderTable(); this._updateInfo(); this._notifyChange();
            }

            clearSelection() {
                if (this.mode === 'multi') this.selected.clear();
                else this.selectedId = null;
                this.renderTable(); this._updateInfo(); this._notifyChange();
            }

            getSelected() { return this.mode === 'multi' ? new Set(this.selected) : this.selectedId; }
            getSelectedCount() { return this.mode === 'multi' ? this.selected.size : (this.selectedId ? 1 : 0); }

            changePerPage() {
                this.perPage = parseInt(document.getElementById(this.uid + '_perpage').value);
                this.page = 1;
                this.renderTable(); this.renderPagination(); this._updateInfo();
            }

            goToPage(page) {
                const tp = Math.ceil(this.filtered.length / this.perPage) || 1;
                if (page < 1 || page > tp) return;
                this.page = page;
                this.renderTable(); this.renderPagination();
            }

            renderPagination() {
                const c = document.getElementById(this.uid + '_pages');
                if (!c) return;
                const tp = Math.ceil(this.filtered.length / this.perPage) || 1;
                if (tp <= 1) { c.innerHTML = ''; return; }
                const u = this.uid, ac = this.accentColor;
                let h = '';
                if (this.page > 1) h += `<button onclick="window._ap['${u}'].goToPage(${this.page-1})" style="padding:4px 8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);border-radius:4px;cursor:pointer;font-size:0.8rem;">&lt;</button>`;
                const mx = 5; let sp = Math.max(1, this.page - Math.floor(mx/2)); let ep = Math.min(tp, sp+mx-1);
                if (ep-sp < mx-1) sp = Math.max(1, ep-mx+1);
                for (let i = sp; i <= ep; i++) {
                    const a = i === this.page;
                    h += `<button onclick="window._ap['${u}'].goToPage(${i})" style="padding:4px 10px;border:1px solid ${a?ac:'var(--border)'};background:${a?ac:'var(--bg-card)'};color:${a?'#000':'var(--text)'};border-radius:4px;cursor:pointer;font-size:0.8rem;font-weight:${a?'600':'400'};">${i}</button>`;
                }
                if (ep < tp) { h += `<span style="color:var(--text-secondary);font-size:0.8rem;">...</span>`; h += `<button onclick="window._ap['${u}'].goToPage(${tp})" style="padding:4px 10px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);border-radius:4px;cursor:pointer;font-size:0.8rem;">${tp}</button>`; }
                if (this.page < tp) h += `<button onclick="window._ap['${u}'].goToPage(${this.page+1})" style="padding:4px 8px;border:1px solid var(--border);background:var(--bg-card);color:var(--text);border-radius:4px;cursor:pointer;font-size:0.8rem;">&gt;</button>`;
                c.innerHTML = h;
            }

            _updateSearchCount() {
                const el = document.getElementById(this.uid + '_count');
                if (el) el.textContent = this.filtered.length;
            }

            _updateInfo() {
                const info = document.getElementById(this.uid + '_info');
                if (!info) return;
                if (this.mode === 'multi') {
                    const cnt = this.selected.size;
                    const freeTotal = this.filtered.filter(a => !this._isDisabled(a)).length;
                    info.innerHTML = `Выбрано: <b style="color:${this.accentColor};">${cnt}</b>`;
                    const sb = document.getElementById(this.uid + '_selbtn');
                    if (sb) sb.textContent = `Выбрать ${this.perPage}`;
                    const sa = document.getElementById(this.uid + '_selallbtn');
                    if (sa) sa.textContent = cnt === freeTotal && freeTotal > 0 ? `Все выбраны (${freeTotal})` : `Выбрать все (${freeTotal})`;
                } else {
                    if (this.selectedId) {
                        const acc = this.accounts.find(a => a.id === this.selectedId);
                        const label = acc ? (acc.username ? `@${acc.username}` : acc.phone) : '';
                        info.innerHTML = `<span style="color:${this.accentColor};">Выбран: ${escapeHtml(label)}</span>`;
                    } else { info.textContent = 'Выберите аккаунт'; }
                }
            }

            _notifyChange() {
                if (this.mode === 'multi') this.onSelectionChange(this.selected, this.selected.size);
                else this.onSelectionChange(this.selectedId, this.selectedId ? 1 : 0);
            }

            destroy() {
                delete window._ap[this.uid];
                if (this.container) this.container.innerHTML = '';
            }
        }
        // ============================================================================

        function copyText(text, el) {
            navigator.clipboard.writeText(text);
            // Анимация клика
            el.style.transform = 'scale(0.95)';
            setTimeout(() => el.style.transform = '', 100);
            // Показываем "Скопировано"
            const tip = document.createElement('span');
            tip.textContent = 'Скопировано';
            tip.style.cssText = 'position:absolute;background:#333;color:#fff;padding:2px 6px;border-radius:4px;font-size:0.7rem;white-space:nowrap;z-index:9999;pointer-events:none;';
            const rect = el.getBoundingClientRect();
            tip.style.left = rect.left + 'px';
            tip.style.top = (rect.top - 22) + 'px';
            document.body.appendChild(tip);
            setTimeout(() => tip.remove(), 800);
        }

        // ============================================================================
        // Безопасная обработка ответов API
        // ============================================================================
        
        async function parseApiResponse(res) {
            // Получаем текст ответа
            const text = await res.text();
            
            // Пробуем распарсить как JSON
            try {
                return JSON.parse(text);
            } catch (e) {
                // Если не JSON, значит это HTML или другая ошибка
                // Возвращаем понятную ошибку
                if (text.includes('<!DOCTYPE') || text.includes('<html')) {
                    // HTML страница ошибки от nginx
                    if (res.status === 502) return { detail: 'Сервер временно недоступен. Попробуйте через минуту.' };
                    if (res.status === 504) return { detail: 'Таймаут сервера. Попробуйте позже или уменьшите размер файла.' };
                    if (res.status === 413) return { detail: 'Файл слишком большой.' };
                    return { detail: `Ошибка сервера (${res.status}). Попробуйте позже.` };
                }
                // Какой-то другой текст
                return { detail: text.substring(0, 200) || 'Неизвестная ошибка сервера' };
            }
        }
        
        function getErrorMessage(data, defaultMsg = 'Произошла ошибка') {
            // Извлекаем понятное сообщение об ошибке
            if (typeof data === 'string') return data;
            if (data.detail) return data.detail;
            if (data.message) return data.message;
            if (data.error) return data.error;
            return defaultMsg;
        }
        
        // ============================================================================
        // MTProxy Status Check
        // ============================================================================
        
        async function checkProxyStatus() {
            const statusEl = document.getElementById('proxyStatus');
            if (!statusEl) return;
            
            // Anination - pulsing while checking
            statusEl.style.background = '#f59e0b';
            statusEl.style.boxShadow = '0 0 6px #f59e0b';
            statusEl.title = 'Проверка...';
            
            try {
                const res = await fetch(`${API_URL}/mtproxy/check`);
                const data = await res.json();
                
                if (data.status === 'online') {
                    statusEl.style.background = '#22c55e';
                    statusEl.style.boxShadow = '0 0 6px #22c55e';
                    statusEl.title = 'Онлайн';
                } else {
                    statusEl.style.background = '#ef4444';
                    statusEl.style.boxShadow = '0 0 6px #ef4444';
                    statusEl.title = 'Офлайн';
                }
            } catch (err) {
                statusEl.style.background = '#ef4444';
                statusEl.style.boxShadow = '0 0 6px #ef4444';
                statusEl.title = 'Ошибка проверки';
            }
        }
        
        async function checkAllProxies() {
            // Проверяем главный прокси
            checkProxyStatus();
            
            // Проверяем все публичные прокси
            if (!cachedMtProxies || cachedMtProxies.length === 0) return;
            
            const proxies = cachedMtProxies.slice(0, 12);
            
            // Ставим все лампочки в режим "проверка"
            proxies.forEach((p, idx) => {
                const lamp = document.getElementById(`proxyLamp${idx}`);
                if (lamp) {
                    lamp.style.background = '#f59e0b';
                    lamp.style.boxShadow = '0 0 4px #f59e0b';
                    lamp.title = 'Проверка...';
                }
            });
            
            // Проверяем каждый прокси через API
            for (let i = 0; i < proxies.length; i++) {
                const p = proxies[i];
                const lamp = document.getElementById(`proxyLamp${i}`);
                if (!lamp) continue;
                
                try {
                    const res = await fetch(`${API_URL}/mtproxy/check-single?host=${encodeURIComponent(p.server)}&port=${p.port}`);
                    const data = await res.json();
                    
                    if (data.status === 'online') {
                        lamp.style.background = '#22c55e';
                        lamp.style.boxShadow = '0 0 4px #22c55e';
                        lamp.title = 'Онлайн';
                    } else {
                        lamp.style.background = '#ef4444';
                        lamp.style.boxShadow = '0 0 4px #ef4444';
                        lamp.title = 'Офлайн';
                    }
                } catch (err) {
                    lamp.style.background = '#ef4444';
                    lamp.style.boxShadow = '0 0 4px #ef4444';
                    lamp.title = 'Ошибка проверки';
                }
                
                // Небольшая задержка чтобы не перегружать сервер
                await new Promise(r => setTimeout(r, 100));
            }
        }
        
        // Автоматическая проверка при открытии страницы MTProxy
        function onMtproxyPageShow() {
            checkProxyStatus();
            loadMTProxyList();
        }
        
        // Маппинг кодов стран на emoji флаги
        const countryFlags = {
            'ru': '🇷🇺', 'de': '🇩🇪', 'nl': '🇳🇱', 'us': '🇺🇸', 'gb': '🇬🇧', 'fr': '🇫🇷',
            'fi': '🇫🇮', 'se': '🇸🇪', 'no': '🇳🇴', 'pl': '🇵🇱', 'ua': '🇺🇦', 'kz': '🇰🇿',
            'lt': '🇱🇹', 'lv': '🇱🇻', 'ee': '🇪🇪', 'by': '🇧🇾', 'md': '🇲🇩', 'ge': '🇬🇪',
            'am': '🇦🇲', 'az': '🇦🇿', 'kg': '🇰🇬', 'uz': '🇺🇿', 'tj': '🇹🇯', 'tm': '🇹🇲',
            'ch': '🇨🇭', 'at': '🇦🇹', 'cz': '🇨🇿', 'sk': '🇸🇰', 'hu': '🇭🇺', 'ro': '🇷🇴',
            'bg': '🇧🇬', 'rs': '🇷🇸', 'hr': '🇭🇷', 'si': '🇸🇮', 'ba': '🇧🇦', 'mk': '🇲🇰',
            'al': '🇦🇱', 'me': '🇲🇪', 'xk': '🇽🇰', 'tr': '🇹🇷', 'gr': '🇬🇷', 'cy': '🇨🇾',
            'it': '🇮🇹', 'es': '🇪🇸', 'pt': '🇵🇹', 'be': '🇧🇪', 'lu': '🇱🇺', 'ie': '🇮🇪',
            'dk': '🇩🇰', 'is': '🇮🇸', 'ca': '🇨🇦', 'au': '🇦🇺', 'nz': '🇳🇿', 'jp': '🇯🇵',
            'kr': '🇰🇷', 'cn': '🇨🇳', 'hk': '🇭🇰', 'tw': '🇹🇼', 'sg': '🇸🇬', 'my': '🇲🇾',
            'th': '🇹🇭', 'vn': '🇻🇳', 'id': '🇮🇩', 'ph': '🇵🇭', 'in': '🇮🇳', 'pk': '🇵🇰',
            'bd': '🇧🇩', 'ir': '🇮🇷', 'iq': '🇮🇶', 'sa': '🇸🇦', 'ae': '🇦🇪', 'il': '🇮🇱',
            'eg': '🇪🇬', 'za': '🇿🇦', 'br': '🇧🇷', 'ar': '🇦🇷', 'mx': '🇲🇽', 'co': '🇨🇴',
            'cl': '🇨🇱', 've': '🇻🇪', 'pe': '🇵🇪'
        };
        
        function getFlag(code) {
            return countryFlags[code?.toLowerCase()] || '🌐';
        }
        
        // Загрузка списка MTProxy с сервера
        let cachedMtProxies = [];
        
        async function loadMTProxyList() {
            const grid = document.getElementById('mtproxyGrid');
            if (!grid) return;
            
            try {
                const res = await fetch('https://fast-helper.ru/sub/mtproxy_list.json');
                const data = await res.json();
                
                if (!data.proxies || data.proxies.length === 0) {
                    grid.innerHTML = '<div style="text-align: center; padding: 20px; color: var(--text-secondary);">Нет доступных прокси</div>';
                    return;
                }
                
                // Сохраняем для проверки
                cachedMtProxies = data.proxies;
                
                // Максимум 12 карточек
                const proxies = data.proxies.slice(0, 12);
                
                grid.innerHTML = proxies.map((p, idx) => {
                    const link = `tg://proxy?server=${p.server}&port=${p.port}&secret=${p.secret}`;
                    const flag = getFlag(p.country_code);
                    const countryName = p.country || 'Неизвестно';
                    const isOnline = p.status === 'online';
                    const statusColor = isOnline ? '#22c55e' : '#ef4444';
                    const statusTitle = isOnline ? 'Онлайн' : 'Офлайн';
                    
                    return `
                        <div class="dashboard-card" style="padding: 16px; display: flex; align-items: center; gap: 12px;">
                            <div style="width: 40px; height: 40px; background: var(--bg-tertiary); border-radius: 10px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; font-size: 20px;">
                                ${flag}
                            </div>
                            <div style="flex: 1; min-width: 0;">
                                <div style="display: flex; align-items: center; gap: 6px;">
                                    <span style="font-size: 14px; font-weight: 500; color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">${escapeHtml(p.server)}</span>
                                    <span id="proxyLamp${idx}" style="width: 8px; height: 8px; border-radius: 50%; background: ${statusColor}; box-shadow: 0 0 4px ${statusColor}; flex-shrink: 0;" title="${statusTitle}"></span>
                                </div>
                                <div style="font-size: 12px; color: var(--text-secondary);">${escapeHtml(countryName)} • Порт ${p.port}</div>
                            </div>
                            <a href="${link}" style="padding: 8px 16px; background: var(--primary); color: #000; font-size: 13px; font-weight: 600; border-radius: 6px; text-decoration: none; white-space: nowrap;">Подключить</a>
                        </div>
                    `;
                }).join('');
                
            } catch (err) {
                grid.innerHTML = '<div style="text-align: center; padding: 20px; color: var(--text-secondary);">Не удалось загрузить список</div>';
            }
        }
        
        // ============================================================================
        // URL Hash Router
        // ============================================================================
        
        // Supported routes:
        // #home, # - Dashboard (or landing if not logged in)
        // #warmup - Прогрев аккаунтов
        // #accounts - Менеджер аккаунтов
        // /proxies - Менеджер прокси
        // /mtproxy - MTProxy
        // /balance - Баланс
        // /admin - Админ панель
        
        function navigateTo(route) {
            history.pushState(null, '', '/' + route);
            handleRoute();
        }
        
        function handleRoute() {
            const path = window.location.pathname.slice(1) || 'home'; // Remove leading /
            
            // Route to appropriate page/tool - allow viewing without auth
            switch(path) {
                case '':
                case 'home':
                    showPage('home');
                    break;
                case 'telegram':
                    showPage('telegram', true);
                    showTelegramTool('overview', false);
                    break;
                case 'warmup':
                    showPage('telegram', true); // true = skip auto tool load
                    showTelegramTool('warmup', false); // false = don't update hash again
                    break;
                case 'accounts':
                    showPage('telegram', true);
                    showTelegramTool('accounts', false);
                    scheduleAccountsLoad();
                    break;
                case 'add-account':
                    showPage('telegram', true);
                    showTelegramTool('add-account', false);
                    break;
                case 'proxies':
                    showPage('telegram', true);
                    showTelegramTool('proxies', false);
                    break;
                case 'projects':
                    showPage('telegram', true);
                    showTelegramTool('projects', false);
                    break;
                case 'converter':
                    showPage('telegram', true);
                    showTelegramTool('converter', false);
                    break;
                case 'files':
                    showPage('telegram', true);
                    showTelegramTool('files', false);
                    break;
                case 'parsing':
                    showPage('telegram', true);
                    showTelegramTool('parsing', false);
                    break;
                case 'chat-parser':
                    showPage('telegram', true);
                    showTelegramTool('chat-parser', false);
                    break;
                case 'inviting':
                    showPage('telegram', true);
                    showTelegramTool('inviting', false);
                    break;
                case 'broadcast':
                    showPage('telegram', true);
                    showTelegramTool('broadcast', false);
                    break;
                case 'groups':
                    showPage('telegram', true);
                    showTelegramTool('groups', false);
                    break;
                case 'monitoring':
                    showPage('telegram', true);
                    showTelegramTool('monitoring', false);
                    break;
                case 'autoposter':
                    showPage('telegram', true);
                    showTelegramTool('autoposter', false);
                    break;
                case 'mtproxy':
                    showPage('mtproxy');
                    onMtproxyPageShow();
                    break;
                case 'balance':
                    showBalancePageDirect(); // Don't update hash again
                    break;
                case 'admin':
                    showAdminPage();
                    break;
                default:
                    // Unknown route, go to home
                    showPage('home');
                    break;
            }
        }
        
        // Version that doesn't update hash (called from router)
        function showBalancePageDirect() {
            document.getElementById('dropdownMenu').classList.remove('show');
            document.getElementById('landingPage').classList.add('hidden');
            document.getElementById('dashboardPage').classList.add('hidden');
            document.getElementById('balancePage').classList.remove('hidden');
            document.getElementById('adminPage').classList.add('hidden');
            document.getElementById('telegramPage').classList.add('hidden');
            document.getElementById('mtproxyPage').classList.add('hidden');
            loadBalance();
            loadTransactionHistory();
            updateProfileHeader();
            loadProfileStats();
        }

        // Hide page loader
        function hidePageLoader() {
            const loader = document.getElementById('pageLoader');
            if (loader) {
                loader.classList.add('hidden');
                setTimeout(() => loader.remove(), 400);
            }
        }
        
        // Init
        document.addEventListener('DOMContentLoaded', async () => {
            const token = localStorage.getItem('token');
            if (token) {
                await checkAuth();
            } else {
                // Гость - показываем сайт, но без авторизации
                document.getElementById('guestButtons').classList.remove('hidden');
                document.getElementById('userMenu').classList.add('hidden');
                // Обрабатываем роут для гостя (может смотреть страницы)
                handleRoute();
            }
            hidePageLoader();
            
            // Listen for browser back/forward
            window.addEventListener('popstate', handleRoute);
            
            // SPA link click handler
            document.addEventListener('click', (e) => {
                const link = e.target.closest('a[href^="/"]');
                if (link && link.getAttribute('href').startsWith('/') && !e.ctrlKey && !e.metaKey && !e.shiftKey && e.button === 0) {
                    e.preventDefault();
                    const path = link.getAttribute('href').slice(1);
                    navigateTo(path || 'home');
                }
            });
            
            // Close modal on overlay click (except upload/auth modals — too easy to lose progress)
            const noCloseOverlayIds = new Set(['addAccountModal', 'uploadSessionModal', 'massUploadModal', 'tdataUploadModal']);
            document.querySelectorAll('.modal-overlay').forEach(overlay => {
                overlay.addEventListener('click', (e) => {
                    if (e.target === overlay && !noCloseOverlayIds.has(overlay.id)) hideAllModals();
                });
            });
            
            // Close dropdown on outside click
            document.addEventListener('click', (e) => {
                if (!e.target.closest('.dropdown')) {
                    document.getElementById('dropdownMenu').classList.remove('show');
                }
            });
        });
        
        function showModal(type) {
            hideAllModals();
            // Если передан полный ID (с "Modal" на конце) — используем как есть
            const elId = type.endsWith('Modal') ? type : type + 'Modal';
            const el = document.getElementById(elId);
            if (el) el.classList.add('show');
            clearErrors();
        }
        
        function closeModal(id) {
            if (id) {
                const el = document.getElementById(id);
                if (el) el.classList.remove('show');
            } else {
                hideAllModals();
            }
        }

        function openModal(id) {
            const el = document.getElementById(id);
            if (el) el.classList.add('show');
        }
        
        function hideAllModals() {
            // Отменяем массовую загрузку если идёт
            if (typeof massUploadAborted !== 'undefined') {
                massUploadAborted = true;
                if (massUploadControllers && massUploadControllers.length) {
                    massUploadControllers.forEach(c => c.abort());
                }
            }
            document.querySelectorAll('.modal-overlay').forEach(m => m.classList.remove('show'));
        }
        
        function showCheckHelpModal() {
            const modal = document.getElementById('checkHelpModal');
            if (modal.classList.contains('show')) {
                modal.classList.remove('show');
            } else {
                hideAllModals();
                modal.classList.add('show');
            }
        }
        
        function toggleDropdown() {
            document.getElementById('dropdownMenu').classList.toggle('show');
        }
        
        function showLanding() {
            document.getElementById('landingPage').classList.remove('hidden');
            document.getElementById('dashboardPage').classList.add('hidden');
            document.getElementById('balancePage').classList.add('hidden');
            document.getElementById('adminPage').classList.add('hidden');
            document.getElementById('telegramPage').classList.add('hidden');
            document.getElementById('guestButtons').classList.remove('hidden');
            document.getElementById('userMenu').classList.add('hidden');
            updateNavLinks('home');
        }
        
        const ADMIN_EMAIL = 'podval.prod@gmail.com';
        let adminToken = null;
        let generatedCodes = [];
        
        function showDashboard(user) {
            document.getElementById('landingPage').classList.add('hidden');
            document.getElementById('dashboardPage').classList.remove('hidden');
            document.getElementById('balancePage').classList.add('hidden');
            document.getElementById('adminPage').classList.add('hidden');
            document.getElementById('telegramPage').classList.add('hidden');
            document.getElementById('guestButtons').classList.add('hidden');
            document.getElementById('userMenu').classList.remove('hidden');
            
            document.getElementById('headerEmail').textContent = 'Личный кабинет';
            document.getElementById('headerAvatar').textContent = user.email[0].toUpperCase();
            
            // Show admin dropdown for admin email
            const adminDropdown = document.getElementById('adminDropdown');
            const warmupMenuItem = document.getElementById('warmupMenuItem');
            const warmupBadge = document.getElementById('warmupBadge');
            const groupsMenuItem = document.getElementById('groupsMenuItem');
            const groupsBadge = document.getElementById('groupsBadge');
            const chatParserMenuItem = document.getElementById('chatParserMenuItem');
            const chatParserBadge = document.getElementById('chatParserBadge');
            if (user.email === ADMIN_EMAIL) {
                if (adminDropdown) adminDropdown.style.display = 'block';
                // Админ видит всё без ограничений
                if (warmupMenuItem) warmupMenuItem.classList.remove('disabled-feature');
                if (warmupMenuItem) warmupMenuItem.onclick = null;
                if (warmupBadge) warmupBadge.style.display = 'none';
                if (groupsMenuItem) groupsMenuItem.classList.remove('disabled-feature');
                if (groupsMenuItem) groupsMenuItem.onclick = null;
                if (groupsBadge) groupsBadge.style.display = 'none';
                if (chatParserMenuItem) chatParserMenuItem.classList.remove('disabled-feature');
                if (chatParserMenuItem) chatParserMenuItem.onclick = null;
                if (chatParserBadge) chatParserBadge.style.display = 'none';
            } else {
                if (adminDropdown) adminDropdown.style.display = 'none';
                // Прогрев аккаунтов доступен всем пользователям
                if (warmupMenuItem) warmupMenuItem.classList.remove('disabled-feature');
                if (warmupMenuItem) warmupMenuItem.onclick = null;
                if (warmupBadge) warmupBadge.style.display = 'none';
                // Прогреватор групп и парсер чатов в разработке
                if (groupsMenuItem) {
                    groupsMenuItem.classList.add('disabled-feature');
                    groupsMenuItem.onclick = (e) => {
                        e.preventDefault();
                        showToast('Функция "Прогреватор групп" временно недоступна. Ведётся доработка.', 'warning');
                    };
                }
                if (groupsBadge) groupsBadge.style.display = '';
                if (chatParserMenuItem) {
                    chatParserMenuItem.classList.add('disabled-feature');
                    chatParserMenuItem.onclick = (e) => {
                        e.preventDefault();
                        showToast('Функция "Парсер чатов" временно недоступна. Ведётся доработка.', 'warning');
                    };
                }
                if (chatParserBadge) chatParserBadge.style.display = '';
            }
            
            updateNavLinks('home');
            hideAllModals();
            loadBalance();
        }
        
        function goHome() {
            if (window.location.pathname !== '/') {
                history.pushState(null, '', '/');
            }
            const token = localStorage.getItem('token');
            if (token) {
                // Просто показываем dashboard без запроса к API
                const user = JSON.parse(localStorage.getItem('user') || '{}');
                showDashboard(user);
            } else {
                showLanding();
            }
        }
        
        function clearErrors() {
            document.querySelectorAll('.error, .success').forEach(el => {
                el.style.display = 'none';
                el.textContent = '';
            });
        }

        /* ========= Universal ugAlert / ugConfirm ========= */
        let _ugModalResolve = null;

        function _ugModalShow(opts) {
            const overlay = document.getElementById('ugModalOverlay');
            document.getElementById('ugModalIcon').textContent = opts.icon || '';
            document.getElementById('ugModalTitle').textContent = opts.title || '';
            document.getElementById('ugModalMessage').textContent = opts.message || '';
            const btnsEl = document.getElementById('ugModalButtons');
            btnsEl.innerHTML = '';
            (opts.buttons || []).forEach(b => {
                const btn = document.createElement('button');
                btn.textContent = b.text;
                btn.className = b.cls || '';
                btn.onclick = () => { _ugModalClose(); if (b.cb) b.cb(); };
                btnsEl.appendChild(btn);
            });
            overlay.classList.add('show');
        }

        function _ugModalClose() {
            document.getElementById('ugModalOverlay').classList.remove('show');
        }

        function ugModalOverlayClick(e) {
            if (e.target === e.currentTarget) _ugModalClose();
        }

        function ugAlert(message, type) {
            return new Promise(resolve => {
                const icons = { success: '✅', error: '❌', warning: '⚠️', info: 'ℹ️' };
                const titles = { success: 'Успешно', error: 'Ошибка', warning: 'Внимание', info: '' };
                const t = type || (message.startsWith('✅') ? 'success' : message.startsWith('❌') ? 'error' : message.startsWith('⚠️') ? 'warning' : 'info');
                const cleanMsg = message.replace(/^[✅❌⚠️ℹ️]\s*/, '');
                _ugModalShow({
                    icon: icons[t] || '',
                    title: titles[t] || '',
                    message: cleanMsg,
                    buttons: [{ text: 'ОК', cls: t === 'error' ? 'ug-modal-btn-danger' : 'ug-modal-btn-ok', cb: resolve }]
                });
            });
        }

        function ugConfirm(message) {
            return new Promise(resolve => {
                const isDanger = /удалить|отменить|остановить|сброс/i.test(message);
                _ugModalShow({
                    icon: isDanger ? '⚠️' : '❓',
                    title: isDanger ? 'Подтвердите действие' : 'Подтверждение',
                    message: message,
                    buttons: [
                        { text: 'Отмена', cls: 'ug-modal-btn-cancel', cb: () => resolve(false) },
                        { text: isDanger ? 'Подтвердить' : 'Да', cls: isDanger ? 'ug-modal-btn-danger' : 'ug-modal-btn-ok', cb: () => resolve(true) }
                    ]
                });
            });
        }
        /* ========= End ugAlert / ugConfirm ========= */

        function showNotification(message, type = 'info') {
            const colors = { success: '#22c55e', error: '#ef4444', warning: '#f59e0b', info: '#3b82f6' };
            const bg = colors[type] || colors.info;
            const div = document.createElement('div');
            div.style.cssText = `position:fixed;top:20px;right:20px;z-index:99999;padding:12px 20px;border-radius:8px;background:${bg};color:#fff;font-size:0.85rem;font-weight:500;box-shadow:0 4px 12px rgba(0,0,0,0.3);max-width:400px;word-wrap:break-word;transition:opacity 0.3s;`;
            div.textContent = message;
            document.body.appendChild(div);
            setTimeout(() => { div.style.opacity = '0'; setTimeout(() => div.remove(), 300); }, 3000);
        }

        function showToast(message, type = 'info') {
            const colors = { error: '#ff6b6b', success: '#f59e0b', info: '#5bc0de' };
            const toast = document.createElement('div');
            toast.textContent = message;
            toast.style.cssText = `position:fixed;top:20px;right:20px;z-index:99999;padding:12px 20px;border-radius:8px;color:#fff;font-size:0.85rem;background:${colors[type] || colors.info};box-shadow:0 4px 12px rgba(0,0,0,.3);opacity:0;transition:opacity 0.3s;max-width:400px;`;
            document.body.appendChild(toast);
            requestAnimationFrame(() => toast.style.opacity = '1');
            setTimeout(() => { toast.style.opacity = '0'; setTimeout(() => toast.remove(), 300); }, 3000);
        }
        
        function showError(elementId, message) {
            const el = document.getElementById(elementId);
            el.textContent = message;
            el.style.display = 'block';
        }
        
        function showSuccess(elementId, message) {
            const el = document.getElementById(elementId);
            el.textContent = message;
            el.style.display = 'block';
        }
        
        function setLoading(btn, loading) {
            if (loading) {
                btn.disabled = true;
                btn.dataset.originalText = btn.textContent;
                btn.innerHTML = '<span class="spinner"></span>Загрузка...';
            } else {
                btn.disabled = false;
                btn.textContent = btn.dataset.originalText;
            }
        }
        
        // API calls
        async function login() {
            const email = document.getElementById('loginEmail').value.trim();
            const password = document.getElementById('loginPassword').value;
            clearErrors();
            
            if (!email || !email.includes('@')) {
                showError('loginError', 'Введите корректный email');
                return;
            }
            if (!password) {
                showError('loginError', 'Введите пароль');
                return;
            }
            
            const btn = event.target;
            setLoading(btn, true);
            
            try {
                const res = await fetch(`${API_URL}/auth/login`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email, password })
                });
                
                const data = await res.json();
                
                if (!res.ok) {
                    throw new Error(data.detail || 'Ошибка входа');
                }
                
                localStorage.setItem('token', data.access_token);
                localStorage.setItem('user', JSON.stringify(data.user));
                showDashboard(data.user);
                handleRoute();
                setTimeout(() => ensureDataCacheLoaded().catch(() => {}), 3000);
                
            } catch (err) {
                let errorMsg = err.message;
                if (errorMsg.includes('Failed to fetch')) {
                    errorMsg = 'Ошибка соединения. Проверьте интернет или попробуйте позже.';
                } else if (errorMsg.includes('NetworkError')) {
                    errorMsg = 'Сетевая ошибка. Проверьте подключение к интернету.';
                }
                showError('loginError', errorMsg);
            } finally {
                setLoading(btn, false);
            }
        }
        
        async function sendCode(mode) {
            currentMode = mode;
            let email, errorId;
            
            if (mode === 'register') {
                email = document.getElementById('registerEmail').value.trim();
                errorId = 'registerError';
            } else {
                email = document.getElementById('forgotEmail').value.trim();
                errorId = 'forgotError';
            }
            
            clearErrors();
            
            if (!email || !email.includes('@')) {
                showError(errorId, 'Введите корректный email');
                return;
            }
            
            currentEmail = email;
            const btn = event.target;
            setLoading(btn, true);
            
            try {
                const endpoint = mode === 'register' ? '/auth/register' : '/auth/forgot-password';
                const res = await fetch(`${API_URL}${endpoint}`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email })
                });
                
                const data = await res.json();
                
                if (!res.ok) {
                    throw new Error(data.detail || 'Ошибка отправки кода');
                }
                
                if (mode === 'register') {
                    showModal('code');
                    document.getElementById('codeEmailHint').textContent = `Код отправлен на ${email}`;
                } else {
                    showModal('reset');
                }
                
            } catch (err) {
                showError(errorId, err.message);
            } finally {
                setLoading(btn, false);
            }
        }
        
        async function verifyCode() {
            const code = document.getElementById('verifyCode').value.trim();
            clearErrors();
            
            if (code.length !== 6) {
                showError('codeError', 'Введите 6-значный код');
                return;
            }
            
            currentCode = code;
            const btn = event.target;
            setLoading(btn, true);
            
            try {
                const res = await fetch(`${API_URL}/auth/verify`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email: currentEmail, code })
                });
                
                const data = await res.json();
                
                if (!res.ok) {
                    throw new Error(data.detail || 'Неверный код');
                }
                
                if (data.needs_password) {
                    showModal('password');
                } else {
                    showSuccess('codeSuccess', 'Код подтверждён!');
                    setTimeout(() => showModal('login'), 1500);
                }
                
            } catch (err) {
                showError('codeError', err.message);
            } finally {
                setLoading(btn, false);
            }
        }
        
        async function setPassword() {
            const password = document.getElementById('newPassword').value;
            const confirmPassword = document.getElementById('confirmPassword').value;
            clearErrors();
            
            if (password.length < 8) {
                showError('passwordError', 'Пароль должен быть минимум 8 символов');
                return;
            }
            if (password !== confirmPassword) {
                showError('passwordError', 'Пароли не совпадают');
                return;
            }
            
            const btn = event.target;
            setLoading(btn, true);
            
            try {
                const res = await fetch(`${API_URL}/auth/set-password`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email: currentEmail, code: currentCode, password })
                });
                
                const data = await res.json();
                
                if (!res.ok) {
                    throw new Error(data.detail || 'Ошибка сохранения пароля');
                }
                
                localStorage.setItem('token', data.access_token);
                localStorage.setItem('user', JSON.stringify(data.user));
                showDashboard(data.user);
                
            } catch (err) {
                showError('passwordError', err.message);
            } finally {
                setLoading(btn, false);
            }
        }
        
        async function resetPassword() {
            const code = document.getElementById('resetCode').value.trim();
            const password = document.getElementById('resetPassword').value;
            clearErrors();
            
            if (code.length !== 6) {
                showError('resetError', 'Введите 6-значный код');
                return;
            }
            if (password.length < 8) {
                showError('resetError', 'Пароль должен быть минимум 8 символов');
                return;
            }
            
            const btn = event.target;
            setLoading(btn, true);
            
            try {
                const res = await fetch(`${API_URL}/auth/reset-password`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email: currentEmail, code, new_password: password })
                });
                
                const data = await res.json();
                
                if (!res.ok) {
                    throw new Error(data.detail || 'Ошибка сброса пароля');
                }
                
                localStorage.setItem('token', data.access_token);
                localStorage.setItem('user', JSON.stringify(data.user));
                showDashboard(data.user);
                
            } catch (err) {
                showError('resetError', err.message);
            } finally {
                setLoading(btn, false);
            }
        }
        
        async function resendCode() {
            clearErrors();
            try {
                const endpoint = currentMode === 'register' ? '/auth/register' : '/auth/forgot-password';
                const res = await fetch(`${API_URL}${endpoint}`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify({ email: currentEmail })
                });
                const data = await res.json();
                if (res.ok) {
                    showSuccess('codeSuccess', data.message || 'Код отправлен повторно');
                } else {
                    showError('codeError', data.detail || 'Ошибка отправки кода');
                }
            } catch (err) {
                showError('codeError', 'Ошибка отправки кода');
            }
        }
        
        // Проверка авторизации - возвращает true если авторизован, иначе показывает модал
        function requireAuth() {
            const token = localStorage.getItem('token');
            if (!token) {
                showModal('login');
                return false;
            }
            return true;
        }
        
        async function checkAuth() {
            const token = localStorage.getItem('token');
            
            try {
                const res = await fetch(`${API_URL}/auth/me`, {
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                
                if (res.ok) {
                    const user = await res.json();
                    localStorage.setItem('user', JSON.stringify(user));
                    showDashboard(user);
                    // Прокси/проекты — в фоне через 3с, не блокируем рассылку
                    setTimeout(() => ensureDataCacheLoaded().catch(() => {}), 3000);
                    // Handle route from URL hash
                    handleRoute();
                    if (location.pathname === '/accounts' || location.pathname.endsWith('/accounts')) {
                        setTimeout(scheduleAccountsLoad, 400);
                        setTimeout(scheduleAccountsLoad, 1500);
                    }
                    // Check for pending route (from before login)
                    const pendingRoute = localStorage.getItem('pendingRoute');
                    if (pendingRoute) {
                        localStorage.removeItem('pendingRoute');
                        navigateTo(pendingRoute);
                    }
                } else if (res.status === 429) {
                    // Rate limited — use cached user data instead of logging out
                    const cached = localStorage.getItem('user');
                    if (cached) {
                        showDashboard(JSON.parse(cached));
                        setTimeout(() => ensureDataCacheLoaded().catch(() => {}), 3000);
                        handleRoute();
                    } else {
                        showToast('Слишком много запросов. Попробуйте позже.', 'error');
                        showLanding();
                    }
                } else {
                    logout();
                }
            } catch (err) {
                showLanding();
            }
        }
        
        function logout() {
            const token = localStorage.getItem('token');
            if (token) {
                fetch(`${API_URL}/auth/logout`, {
                    method: 'POST',
                    headers: { 'Authorization': `Bearer ${token}` }
                }).catch(() => {});
            }
            localStorage.removeItem('token');
            localStorage.removeItem('user');
            showLanding();
        }
        
        function showBalancePage() {
            document.getElementById('dropdownMenu').classList.remove('show');
            document.getElementById('landingPage').classList.add('hidden');
            document.getElementById('dashboardPage').classList.add('hidden');
            document.getElementById('balancePage').classList.remove('hidden');
            document.getElementById('adminPage').classList.add('hidden');
            document.getElementById('telegramPage').classList.add('hidden');
            // Update URL
            if (window.location.pathname !== '/balance') {
                history.pushState(null, '', '/balance');
            }
            loadBalance();
            loadTransactionHistory();
            updateProfileHeader();
            loadProfileStats();
        }
        
        // Profile Tab Switching
        function switchProfileTab(tabName) {
            // Update tab buttons - find by data attribute
            document.querySelectorAll('.profile-tab').forEach(tab => {
                tab.classList.remove('active');
                if (tab.dataset.tab === tabName) {
                    tab.classList.add('active');
                }
            });
            
            // Update sections
            document.querySelectorAll('.profile-section').forEach(sec => sec.classList.remove('active'));
            const sectionMap = {
                'overview': 'profileOverview',
                'balance': 'profileBalance',
                'stats': 'profileStats',
                'tariff': 'profileTariffSection',
                'autostart': 'profileAutostart'
            };
            const sectionId = sectionMap[tabName];
            if (sectionId) {
                document.getElementById(sectionId).classList.add('active');
            }
            
            // Sync balance values
            var bal = document.getElementById('userBalancePage');
            var bal2 = document.getElementById('userBalancePage2');
            if (bal && bal2) bal2.textContent = bal.textContent;
            
            // Init charts when switching to stats
            if (tabName === 'stats') {
                setTimeout(function() { initProfileCharts(); }, 300);
            }
            
            // Load autostart schedules when switching to autostart tab
            if (tabName === 'autostart') {
                loadAutostartSchedules();
            }
        }
        
        // Update profile header with user data
        function updateProfileHeader() {
            const userStr = localStorage.getItem('user');
            const user = userStr ? JSON.parse(userStr) : null;
            const email = user?.email || 'user@example.com';
            const avatar = email.charAt(0).toUpperCase();
            
            document.getElementById('profileEmail').textContent = email;
            // Hamster avatar - no text needed
            document.getElementById('profileName').textContent = email.split('@')[0];
        }
        
        // Profile charts instance
        let profileActivityChartInstance = null;
        
        // Cached stats data for charts
        let profileStatsData = null;
        
        // Mini loader for stat values
        function getStatLoaderHtml() {
            return '<div class="abstergo-loader" style="transform:scale(0.4);margin:-10px 0;"><div></div><div></div><div></div></div>';
        }
        
        // Handle 401 auth error - session expired
        function handleAuthExpired() {
            showNotification('Сессия истекла. Пожалуйста, войдите снова.', 'warning');
            setTimeout(() => {
                logout();
            }, 1500);
        }
        
        // ==================== AUTOSTART MANAGER ====================
        // v2 - 01.04.2026 09:15
        
        // Attach button listener immediately
        (function() {
            function attachAddBtn() {
                var btn = document.getElementById('btnAddAutostart');
                if (!btn) { setTimeout(attachAddBtn, 200); return; }
                btn.addEventListener('click', function(e) {
                    e.preventDefault();
                    if (typeof window.showAddAutostartModal === 'function') {
                        window.showAddAutostartModal();
                    } else {
                        alert('ERROR: showAddAutostartModal is not defined! Page JS broken.');
                    }
                });
            }
            attachAddBtn();
        })();
        
        async function loadAutostartSchedules() {
            const token = localStorage.getItem('token');
            if (!token) return;
            
            const container = document.getElementById('autostartSchedulesList');
            container.innerHTML = '<div style="text-align:center;padding:40px;color:var(--text-secondary);"><i class="fa-solid fa-spinner fa-spin" style="font-size:1.5rem;margin-bottom:10px;display:block;"></i>Загрузка...</div>';
            
            try {
                const res = await fetch(API_URL + '/autostart/schedules', {
                    headers: { 'Authorization': 'Bearer ' + token }
                });
                
                if (!res.ok) throw new Error('Failed to load');
                const schedules = await res.json();
                
                if (schedules.length === 0) {
                    container.innerHTML = `
                        <div style="text-align:center;padding:50px;color:var(--text-secondary);">
                            <i class="fa-solid fa-clock-rotate-left" style="font-size:3rem;margin-bottom:16px;display:block;opacity:0.5;"></i>
                            <div style="font-size:1rem;margin-bottom:8px;">Нет настроенных автозапусков</div>
                            <div style="font-size:0.85rem;opacity:0.7;">Нажмите "Добавить" чтобы настроить автоматический перезапуск задачи</div>
                        </div>
                    `;
                    return;
                }
                
                container.innerHTML = schedules.map(s => renderAutostartCard(s)).join('');
            } catch (e) {
                console.error('Load autostart error:', e);
                container.innerHTML = '<div style="text-align:center;padding:40px;color:#ef4444;">Ошибка загрузки</div>';
            }
        }
        
        function renderAutostartCard(schedule) {
            const jobTypeLabel = schedule.job_type === 'warmup' ? 'Прогрев' : 'Инвайтинг';
            const jobTypeIcon = schedule.job_type === 'warmup' ? 'fa-fire' : 'fa-paper-plane';
            const jobName = schedule.job_name || `Задача #${schedule.job_id}`;
            const statusColor = schedule.enabled ? '#22c55e' : '#6b7280';
            const statusText = schedule.enabled ? 'Активен' : 'Выключен';
            
            let nextRunText = '—';
            if (schedule.enabled && schedule.next_run_at) {
                const diffSec = schedule.seconds_until_run;
                if (diffSec != null && diffSec > 0) {
                    const hours = Math.floor(diffSec / 3600);
                    const mins = Math.floor((diffSec % 3600) / 60);
                    // Вычисляем локальное время запуска из серверного countdown
                    const launchLocal = new Date(Date.now() + diffSec * 1000);
                    const launchTime = launchLocal.toLocaleTimeString('ru-RU', {hour:'2-digit', minute:'2-digit'});
                    nextRunText = hours > 0 ? `через ${hours}ч ${mins}м (в ${launchTime})` : `через ${mins}м (в ${launchTime})`;
                } else {
                    nextRunText = 'скоро';
                }
            }
            
            // Format last run time (timezone-aware from server)
            let lastRunText = '';
            if (schedule.last_run_at) {
                const lastRun = new Date(schedule.last_run_at);
                lastRunText = lastRun.toLocaleString('ru-RU', {day:'2-digit', month:'2-digit', hour:'2-digit', minute:'2-digit'});
            }
            
            let modeText = '';
            if (schedule.job_type === 'warmup') {
                modeText = schedule.warmup_mode === 'next_day' ? 'След. день' : 'Этот день';
            }
            
            return `
                <div class="autostart-card" style="background:var(--bg-card);border:1px solid var(--border);border-radius:10px;padding:16px;">
                    <div style="display:flex;justify-content:space-between;align-items:flex-start;gap:12px;flex-wrap:wrap;">
                        <div style="flex:1;min-width:200px;">
                            <div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;">
                                <i class="fa-solid ${jobTypeIcon}" style="color:var(--primary);"></i>
                                <span style="font-weight:600;">${escapeHtml(jobName)}</span>
                                <span style="font-size:0.75rem;padding:2px 8px;border-radius:4px;background:${schedule.job_type === 'warmup' ? 'rgba(249,115,22,0.15)' : 'rgba(59,130,246,0.15)'};color:${schedule.job_type === 'warmup' ? '#f97316' : '#3b82f6'};">${jobTypeLabel}</span>
                            </div>
                            <div style="display:flex;gap:16px;font-size:0.8rem;color:var(--text-secondary);flex-wrap:wrap;">
                                <span><i class="fa-solid fa-circle" style="font-size:6px;color:${statusColor};margin-right:4px;"></i>${statusText}</span>
                                <span><i class="fa-regular fa-clock" style="margin-right:4px;"></i>Интервал: ${schedule.interval_hours_min}-${schedule.interval_hours_max}ч</span>
                                ${modeText ? `<span><i class="fa-solid fa-arrow-right" style="margin-right:4px;"></i>${modeText}</span>` : ''}
                                ${schedule.enabled ? `<span style="cursor:pointer;" onclick="window.showSetRunTimeModal(${schedule.id}, '${escapeHtml(jobName).replace(/'/g, "\\'")}')" title="Нажмите чтобы изменить"><i class="fa-solid fa-play" style="margin-right:4px;color:#22c55e;"></i>${nextRunText}</span>` : ''}
                                ${lastRunText ? `<span title="Последний запуск"><i class="fa-solid fa-history" style="margin-right:4px;color:#8b5cf6;"></i>${lastRunText}</span>` : ''}
                            </div>
                        </div>
                        <div style="display:flex;align-items:center;gap:8px;">
                            <button onclick="window.showSetRunTimeModal(${schedule.id}, '${escapeHtml(jobName).replace(/'/g, "\\'")}')" style="background:transparent;border:1px solid rgba(59,130,246,0.3);border-radius:6px;padding:6px 10px;color:#3b82f6;cursor:pointer;font-size:0.8rem;" title="Запустить через...">
                                <i class="fa-regular fa-clock"></i>
                            </button>
                            <label class="toggle-switch" style="margin:0;">
                                <input type="checkbox" ${schedule.enabled ? 'checked' : ''} onchange="window.toggleAutostartEnabled(${schedule.id}, this.checked)">
                                <span class="toggle-slider"></span>
                            </label>
                            ${schedule.job_type === 'warmup' ? `
                            <select onchange="window.updateAutostartMode(${schedule.id}, this.value)" style="background:var(--bg-hover);border:1px solid var(--border);border-radius:6px;padding:6px 10px;font-size:0.8rem;color:var(--text-primary);cursor:pointer;">
                                <option value="next_day" ${schedule.warmup_mode === 'next_day' ? 'selected' : ''}>След. день</option>
                                <option value="same_day" ${schedule.warmup_mode === 'same_day' ? 'selected' : ''}>Этот день</option>
                            </select>
                            ` : ''}
                            <button onclick="window.deleteAutostartSchedule(${schedule.id})" style="background:transparent;border:1px solid rgba(239,68,68,0.3);border-radius:6px;padding:6px 10px;color:#ef4444;cursor:pointer;font-size:0.8rem;" title="Удалить">
                                <i class="fa-solid fa-trash"></i>
                            </button>
                        </div>
                    </div>
                </div>
            `;
        }
        
        window.toggleAutostartEnabled = async function(scheduleId, enabled) {
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(API_URL + '/autostart/schedules/' + scheduleId, {
                    method: 'PUT',
                    headers: { 
                        'Authorization': 'Bearer ' + token,
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ enabled })
                });
                if (!res.ok) throw new Error('Update failed');
                showNotification(enabled ? 'Автозапуск включён' : 'Автозапуск выключен', 'success');
                loadAutostartSchedules();
            } catch (e) {
                showNotification('Ошибка обновления', 'error');
                loadAutostartSchedules();
            }
        };
        
        window.updateAutostartMode = async function(scheduleId, mode) {
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(API_URL + '/autostart/schedules/' + scheduleId, {
                    method: 'PUT',
                    headers: { 
                        'Authorization': 'Bearer ' + token,
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ warmup_mode: mode })
                });
                if (!res.ok) throw new Error('Update failed');
                showNotification('Режим изменён', 'success');
            } catch (e) {
                showNotification('Ошибка обновления', 'error');
                loadAutostartSchedules();
            }
        };
        
        window.deleteAutostartSchedule = async function(scheduleId) {
            if (!confirm('Удалить этот автозапуск?')) return;
            
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(API_URL + '/autostart/schedules/' + scheduleId, {
                    method: 'DELETE',
                    headers: { 'Authorization': 'Bearer ' + token }
                });
                if (!res.ok) throw new Error('Delete failed');
                showNotification('Автозапуск удалён', 'success');
                loadAutostartSchedules();
            } catch (e) {
                showNotification('Ошибка удаления', 'error');
            }
        };
        
        // Модал для изменения времени запуска
        window.showSetRunTimeModal = function(scheduleId, jobName) {
            const modalHtml = `
                <div id="setRunTimeModal" class="modal-overlay show" style="display:flex;">
                    <div class="modal" style="max-width:400px;">
                        <div class="modal-header">
                            <h3>Запустить через...</h3>
                            <button onclick="window.closeSetRunTimeModal()" class="modal-close">&times;</button>
                        </div>
                        <div class="modal-body" style="padding:20px;">
                            <div style="margin-bottom:16px;font-size:0.9rem;color:var(--text-secondary);">
                                Задача: <strong style="color:var(--text-primary);">${jobName}</strong>
                            </div>
                            
                            <div style="margin-bottom:16px;">
                                <label style="display:block;margin-bottom:6px;font-size:0.85rem;color:var(--text-secondary);">Запустить через</label>
                                <div style="display:flex;gap:10px;align-items:center;">
                                    <input type="number" id="runAfterValue" value="5" min="1" max="1440" style="width:80px;background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:0.9rem;color:var(--text-primary);text-align:center;">
                                    <select id="runAfterUnit" style="flex:1;background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:0.9rem;color:var(--text-primary);">
                                        <option value="minutes">минут</option>
                                        <option value="hours">часов</option>
                                    </select>
                                </div>
                            </div>
                            
                            <div style="display:flex;gap:8px;flex-wrap:wrap;">
                                <button onclick="window.setQuickRunTime(${scheduleId}, 5)" style="flex:1;background:var(--bg-hover);border:1px solid var(--border);border-radius:6px;padding:8px 12px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;">5 мин</button>
                                <button onclick="window.setQuickRunTime(${scheduleId}, 15)" style="flex:1;background:var(--bg-hover);border:1px solid var(--border);border-radius:6px;padding:8px 12px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;">15 мин</button>
                                <button onclick="window.setQuickRunTime(${scheduleId}, 30)" style="flex:1;background:var(--bg-hover);border:1px solid var(--border);border-radius:6px;padding:8px 12px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;">30 мин</button>
                                <button onclick="window.setQuickRunTime(${scheduleId}, 60)" style="flex:1;background:var(--bg-hover);border:1px solid var(--border);border-radius:6px;padding:8px 12px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;">1 час</button>
                            </div>
                        </div>
                        <div class="modal-footer" style="padding:16px 20px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:10px;">
                            <button onclick="window.closeSetRunTimeModal()" style="background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 20px;color:var(--text-primary);cursor:pointer;">Отмена</button>
                            <button onclick="window.applyRunTime(${scheduleId})" class="primary-btn" style="padding:10px 20px;">Применить</button>
                        </div>
                    </div>
                </div>
            `;
            
            document.body.insertAdjacentHTML('beforeend', modalHtml);
        };
        
        window.closeSetRunTimeModal = function() {
            const modal = document.getElementById('setRunTimeModal');
            if (modal) modal.remove();
        };
        
        window.setQuickRunTime = async function(scheduleId, minutes) {
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(API_URL + '/autostart/schedules/' + scheduleId, {
                    method: 'PUT',
                    headers: { 
                        'Authorization': 'Bearer ' + token,
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({ run_after_minutes: minutes })
                });
                if (!res.ok) throw new Error('Update failed');
                showNotification(`Запуск через ${minutes} мин`, 'success');
                window.closeSetRunTimeModal();
                loadAutostartSchedules();
            } catch (e) {
                showNotification('Ошибка обновления', 'error');
            }
        };
        
        window.applyRunTime = async function(scheduleId) {
            const value = parseInt(document.getElementById('runAfterValue').value) || 5;
            const unit = document.getElementById('runAfterUnit').value;
            const minutes = unit === 'hours' ? value * 60 : value;
            
            if (minutes < 1 || minutes > 43200) {
                showNotification('Время должно быть от 1 мин до 30 дней', 'warning');
                return;
            }
            
            await window.setQuickRunTime(scheduleId, minutes);
        };
        
        // Make autostart functions global for onclick handlers
        window.showAddAutostartModal = async function() {
            const token = localStorage.getItem('token');
            
            // Load available jobs
            let availableJobs;
            try {
                const url = API_URL + '/autostart/available-jobs';
                const res = await fetch(url, {
                    headers: { 'Authorization': 'Bearer ' + token }
                });
                if (!res.ok) throw new Error('Failed to load jobs: ' + res.status);
                availableJobs = await res.json();
            } catch (e) {
                showNotification('Не удалось загрузить список задач', 'error');
                return;
            }
            
            const warmupOptions = availableJobs.warmup.map(j => 
                `<option value="warmup:${j.id}">${j.name || 'Прогрев день ' + j.target_day} (${j.total_accounts} акк.)</option>`
            ).join('');
            
            const inviteOptions = availableJobs.invite.map(j => 
                `<option value="invite:${j.id}">${j.name} (${j.total_users} польз.)</option>`
            ).join('');
            
            if (!warmupOptions && !inviteOptions) {
                showNotification('Нет доступных задач для добавления', 'warning');
                return;
            }
            
            const modalHtml = `
                <div id="addAutostartModal" class="modal-overlay show" style="display:flex;">
                    <div class="modal" style="max-width:450px;">
                        <div class="modal-header">
                            <h3>Добавить автозапуск</h3>
                            <button onclick="window.closeAddAutostartModal()" class="modal-close">&times;</button>
                        </div>
                        <div class="modal-body" style="padding:20px;">
                            <div style="margin-bottom:16px;">
                                <label style="display:block;margin-bottom:6px;font-size:0.85rem;color:var(--text-secondary);">Выберите задачу</label>
                                <select id="autostartJobSelect" style="width:100%;background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:0.9rem;color:var(--text-primary);" onchange="window.onAutostartJobChange()">
                                    <option value="">— Выберите —</option>
                                    ${warmupOptions ? `<optgroup label="Прогрев">${warmupOptions}</optgroup>` : ''}
                                    ${inviteOptions ? `<optgroup label="Инвайтинг">${inviteOptions}</optgroup>` : ''}
                                </select>
                            </div>
                            
                            <div id="warmupModeSection" style="margin-bottom:16px;display:none;">
                                <label style="display:block;margin-bottom:6px;font-size:0.85rem;color:var(--text-secondary);">Режим прогрева</label>
                                <select id="autostartWarmupMode" style="width:100%;background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:0.9rem;color:var(--text-primary);">
                                    <option value="next_day">Следующий день (увеличивать target_day)</option>
                                    <option value="same_day">Тот же день (повторять)</option>
                                </select>
                            </div>
                            
                            <div style="margin-bottom:16px;">
                                <label style="display:block;margin-bottom:6px;font-size:0.85rem;color:var(--text-secondary);">Первый запуск</label>
                                <div style="display:flex;gap:10px;align-items:center;margin-bottom:8px;">
                                    <label style="display:flex;align-items:center;gap:6px;cursor:pointer;">
                                        <input type="radio" name="firstRunType" value="interval" checked onchange="window.toggleFirstRunType()">
                                        <span style="font-size:0.85rem;">По интервалу (24-26ч)</span>
                                    </label>
                                    <label style="display:flex;align-items:center;gap:6px;cursor:pointer;">
                                        <input type="radio" name="firstRunType" value="custom" onchange="window.toggleFirstRunType()">
                                        <span style="font-size:0.85rem;">Вручную</span>
                                    </label>
                                </div>
                                <div id="customFirstRunSection" style="display:none;margin-top:8px;">
                                    <div style="display:flex;gap:10px;align-items:center;">
                                        <span style="font-size:0.85rem;color:var(--text-secondary);">Через</span>
                                        <input type="number" id="autostartFirstRunValue" value="5" min="1" max="1440" style="width:70px;background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:8px 10px;font-size:0.9rem;color:var(--text-primary);text-align:center;">
                                        <select id="autostartFirstRunUnit" style="background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:8px 10px;font-size:0.9rem;color:var(--text-primary);">
                                            <option value="minutes">минут</option>
                                            <option value="hours">часов</option>
                                        </select>
                                    </div>
                                </div>
                            </div>
                            
                            <div style="margin-bottom:16px;">
                                <label style="display:block;margin-bottom:6px;font-size:0.85rem;color:var(--text-secondary);">Интервал между запусками (часы)</label>
                                <div style="display:flex;gap:10px;align-items:center;">
                                    <input type="number" id="autostartMinHours" value="24" min="1" max="168" style="flex:1;background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:0.9rem;color:var(--text-primary);">
                                    <span style="color:var(--text-secondary);">—</span>
                                    <input type="number" id="autostartMaxHours" value="26" min="1" max="168" style="flex:1;background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 12px;font-size:0.9rem;color:var(--text-primary);">
                                </div>
                                <div style="font-size:0.75rem;color:var(--text-secondary);margin-top:4px;">Случайное время между min и max часами для последующих запусков</div>
                            </div>
                        </div>
                        <div class="modal-footer" style="padding:16px 20px;border-top:1px solid var(--border);display:flex;justify-content:flex-end;gap:10px;">
                            <button onclick="window.closeAddAutostartModal()" style="background:var(--bg-hover);border:1px solid var(--border);border-radius:8px;padding:10px 20px;color:var(--text-primary);cursor:pointer;">Отмена</button>
                            <button onclick="window.createAutostartSchedule()" class="primary-btn" style="padding:10px 20px;">Добавить</button>
                        </div>
                    </div>
                </div>
            `;
            
            document.body.insertAdjacentHTML('beforeend', modalHtml);
        };
        
        window.onAutostartJobChange = function() {
            const select = document.getElementById('autostartJobSelect');
            const warmupSection = document.getElementById('warmupModeSection');
            if (select.value.startsWith('warmup:')) {
                warmupSection.style.display = 'block';
            } else {
                warmupSection.style.display = 'none';
            }
        };
        
        window.toggleFirstRunType = function() {
            const customSection = document.getElementById('customFirstRunSection');
            const selectedType = document.querySelector('input[name="firstRunType"]:checked').value;
            customSection.style.display = selectedType === 'custom' ? 'block' : 'none';
        };
        
        window.closeAddAutostartModal = function() {
            const modal = document.getElementById('addAutostartModal');
            if (modal) modal.remove();
        };
        
        window.createAutostartSchedule = async function() {
            const token = localStorage.getItem('token');
            const jobSelect = document.getElementById('autostartJobSelect');
            const warmupMode = document.getElementById('autostartWarmupMode');
            const minHours = document.getElementById('autostartMinHours');
            const maxHours = document.getElementById('autostartMaxHours');
            
            if (!jobSelect.value) {
                showNotification('Выберите задачу', 'warning');
                return;
            }
            
            const [jobType, jobId] = jobSelect.value.split(':');
            
            const data = {
                job_type: jobType,
                job_id: parseInt(jobId),
                interval_hours_min: parseInt(minHours.value) || 24,
                interval_hours_max: parseInt(maxHours.value) || 26
            };
            
            if (jobType === 'warmup') {
                data.warmup_mode = warmupMode.value;
            }
            
            // Проверяем тип первого запуска
            const firstRunType = document.querySelector('input[name="firstRunType"]:checked');
            if (firstRunType && firstRunType.value === 'custom') {
                const firstRunValue = parseInt(document.getElementById('autostartFirstRunValue').value) || 5;
                const firstRunUnit = document.getElementById('autostartFirstRunUnit').value;
                const runAfterMinutes = firstRunUnit === 'hours' ? firstRunValue * 60 : firstRunValue;
                
                if (runAfterMinutes < 1 || runAfterMinutes > 43200) {
                    showNotification('Время должно быть от 1 мин до 30 дней', 'warning');
                    return;
                }
                
                data.run_after_minutes = runAfterMinutes;
            }
            
            try {
                const res = await fetch(API_URL + '/autostart/schedules', {
                    method: 'POST',
                    headers: {
                        'Authorization': 'Bearer ' + token,
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify(data)
                });
                
                if (!res.ok) {
                    const err = await res.json();
                    throw new Error(err.detail || 'Create failed');
                }
                
                showNotification('Автозапуск создан', 'success');
                window.closeAddAutostartModal();
                loadAutostartSchedules();
            } catch (e) {
                showNotification(e.message || 'Ошибка создания', 'error');
            }
        };
        
        // ==================== END AUTOSTART MANAGER ====================

        // Load profile stats and charts
        async function loadProfileStats() {
            var token = localStorage.getItem('token');
            if (!token) return;
            
            // Show loaders in stat cards
            const statIds = ['statAccounts', 'statInvites', 'statAudience', 'statTasks', 'statTotalInvites', 'statTotalMessages', 'statTotalParsed', 'statTotalTasks'];
            statIds.forEach(id => {
                const el = document.getElementById(id);
                if (el) el.innerHTML = getStatLoaderHtml();
            });
            
            try {
                // Load stats from API
                const res = await fetch(API_URL + '/user/stats', {
                    headers: { 'Authorization': 'Bearer ' + token }
                });
                
                // Handle expired token
                if (res.status === 401) {
                    handleAuthExpired();
                    return;
                }
                
                if (res.ok) {
                    const data = await res.json();
                    profileStatsData = data;
                    
                    // Update Overview tab stats
                    document.getElementById('statAccounts').textContent = data.accounts || 0;
                    document.getElementById('statInvites').textContent = data.invites || 0;
                    document.getElementById('statAudience').textContent = data.audience || 0;
                    document.getElementById('statTasks').textContent = data.tasks_completed || 0;
                    
                    // Update Statistics tab stats
                    document.getElementById('statTotalInvites').textContent = data.invites || 0;
                    document.getElementById('statTotalMessages').textContent = data.messages_sent || 0;
                    document.getElementById('statTotalParsed').textContent = data.audience || 0;
                    document.getElementById('statTotalTasks').textContent = data.tasks_completed || 0;
                } else {
                    // Other error - show zeros
                    statIds.forEach(id => {
                        const el = document.getElementById(id);
                        if (el) el.textContent = '—';
                    });
                }
            } catch (err) {
                console.error('loadProfileStats error:', err);
                // Network error - show dashes
                statIds.forEach(id => {
                    const el = document.getElementById(id);
                    if (el) el.textContent = '—';
                });
            }
            
            // Load recent activity
            loadProfileActivity();
        }
        
        // Load recent activity for table
        async function loadProfileActivity() {
            var token = localStorage.getItem('token');
            if (!token) return;
            
            const tableBody = document.getElementById('profileActivityTable');
            if (!tableBody) return;
            
            // Show loader in table
            tableBody.innerHTML = `<tr>
                <td colspan="4" style="text-align:center;padding:30px;">
                    ${getInlineLoaderHtml('Загрузка активности')}
                </td>
            </tr>`;
            
            try {
                const res = await fetch(API_URL + '/user/activity?limit=10', {
                    headers: { 'Authorization': 'Bearer ' + token }
                });
                
                // Handle expired token
                if (res.status === 401) {
                    handleAuthExpired();
                    return;
                }
                
                if (res.ok) {
                    const data = await res.json();
                    const activities = data.activities || [];
                    
                    if (activities.length === 0) {
                        tableBody.innerHTML = `<tr>
                            <td colspan="4" style="text-align:center;color:#6c7293;padding:30px;">
                                <i class="fa-solid fa-clock-rotate-left" style="font-size:2rem;margin-bottom:10px;display:block;opacity:0.5;"></i>
                                История действий пуста
                            </td>
                        </tr>`;
                        return;
                    }
                    
                    tableBody.innerHTML = activities.map(a => `
                        <tr>
                            <td>${a.date ? new Date(a.date).toLocaleDateString('ru-RU') : '—'}</td>
                            <td>${escapeHtml(a.action)}</td>
                            <td>${escapeHtml(a.account)}</td>
                            <td>${escapeHtml(a.result)}</td>
                        </tr>
                    `).join('');
                } else {
                    tableBody.innerHTML = `<tr>
                        <td colspan="4" style="text-align:center;color:#6c7293;padding:30px;">
                            Не удалось загрузить активность
                        </td>
                    </tr>`;
                }
            } catch (err) {
                console.error('loadProfileActivity error:', err);
                tableBody.innerHTML = `<tr>
                    <td colspan="4" style="text-align:center;color:#6c7293;padding:30px;">
                        Ошибка загрузки
                    </td>
                </tr>`;
            }
        }
        
        function initProfileCharts() {
            if (typeof Chart === 'undefined') return;
            
            // Destroy old
            if (profileActivityChartInstance) { profileActivityChartInstance.destroy(); profileActivityChartInstance = null; }
            
            // Initialize selected month to current
            const now = new Date();
            selectedChartYear = now.getFullYear();
            selectedChartMonth = now.getMonth();
            
            // Create fresh canvas
            var wrap1 = document.getElementById('chartWrap1');
            if (!wrap1) { console.log('No chart wrap found'); return; }
            if (wrap1.offsetWidth === 0) { console.log('Chart wrap has 0 width, skipping'); return; }
            
            wrap1.innerHTML = '<canvas></canvas>';
            var c1 = wrap1.querySelector('canvas');
            
            Chart.defaults.color = '#6c7293';
            Chart.defaults.borderColor = '#2a2a2a';
            
            // Create chart with current filter
            updateActivityChart(c1, currentChartFilter);
            
            console.log('Chart created OK, wrap width=' + wrap1.offsetWidth);
        }
        
        // Current chart filter and month
        let currentChartFilter = 'warmup';
        let selectedChartYear = new Date().getFullYear();
        let selectedChartMonth = new Date().getMonth();
        
        // Filter config with colors and labels
        // warmup = accounts warmed, parser = people parsed, inviting = people invited
        const chartFilterConfig = {
            warmup: { label: 'Прогретых аккаунтов', color: 'rgba(245, 158, 11, 0.8)', fields: ['warmup'] },
            parser: { label: 'Спарсено людей', color: 'rgba(245, 158, 11, 0.8)', fields: ['parser'] },
            inviting: { label: 'Приглашено людей', color: 'rgba(245, 158, 11, 0.8)', fields: ['inviting'] },
            broadcast: { label: 'Сообщений отправлено', color: 'rgba(245, 158, 11, 0.8)', fields: ['broadcast'] }
        };
        
        // Change month for chart
        function changeChartMonth(delta) {
            selectedChartMonth += delta;
            
            if (selectedChartMonth > 11) {
                selectedChartMonth = 0;
                selectedChartYear++;
            } else if (selectedChartMonth < 0) {
                selectedChartMonth = 11;
                selectedChartYear--;
            }
            
            // Limit to reasonable range (2024-2028)
            const now = new Date();
            if (selectedChartYear < 2024 || (selectedChartYear === 2024 && selectedChartMonth < 0)) {
                selectedChartYear = 2024;
                selectedChartMonth = 0;
            }
            if (selectedChartYear > now.getFullYear() || (selectedChartYear === now.getFullYear() && selectedChartMonth > now.getMonth())) {
                selectedChartYear = now.getFullYear();
                selectedChartMonth = now.getMonth();
            }
            
            // Update chart
            var wrap1 = document.getElementById('chartWrap1');
            if (!wrap1) return;
            var canvas = wrap1.querySelector('canvas');
            if (!canvas) {
                wrap1.innerHTML = '<canvas></canvas>';
                canvas = wrap1.querySelector('canvas');
            }
            
            if (profileActivityChartInstance) {
                profileActivityChartInstance.destroy();
                profileActivityChartInstance = null;
            }
            
            updateActivityChart(canvas, currentChartFilter);
        }
        
        // Reset chart to current month
        function resetChartToToday() {
            const now = new Date();
            selectedChartYear = now.getFullYear();
            selectedChartMonth = now.getMonth();
            
            // Update chart
            var wrap1 = document.getElementById('chartWrap1');
            if (!wrap1) return;
            var canvas = wrap1.querySelector('canvas');
            if (!canvas) {
                wrap1.innerHTML = '<canvas></canvas>';
                canvas = wrap1.querySelector('canvas');
            }
            
            if (profileActivityChartInstance) {
                profileActivityChartInstance.destroy();
                profileActivityChartInstance = null;
            }
            
            updateActivityChart(canvas, currentChartFilter);
        }
        
        function setChartFilter(filter) {
            currentChartFilter = filter;
            
            // Update button states
            document.querySelectorAll('.chart-filter-btn').forEach(btn => {
                btn.classList.toggle('active', btn.dataset.filter === filter);
            });
            
            // Update chart
            var wrap1 = document.getElementById('chartWrap1');
            if (!wrap1) return;
            var canvas = wrap1.querySelector('canvas');
            if (!canvas) {
                wrap1.innerHTML = '<canvas></canvas>';
                canvas = wrap1.querySelector('canvas');
            }
            
            if (profileActivityChartInstance) {
                profileActivityChartInstance.destroy();
                profileActivityChartInstance = null;
            }
            
            updateActivityChart(canvas, filter);
        }
        
        // Store full dates for tooltip access
        let chartFullDates = [];
        
        // Get days in month (handles leap years)
        function getDaysInMonth(year, month) {
            return new Date(year, month + 1, 0).getDate();
        }
        
        function updateActivityChart(canvas, filter) {
            const config = chartFilterConfig[filter] || chartFilterConfig.all;
            
            // Generate labels for selected calendar month
            const labels = [];
            chartFullDates = []; // Reset for tooltip
            const chartData = [];
            const dataMap = {};
            
            const monthNamesFull = ['январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь'];
            
            // Build data map from API data
            if (profileStatsData && profileStatsData.activity_by_day) {
                profileStatsData.activity_by_day.forEach(item => {
                    let count = 0;
                    config.fields.forEach(field => {
                        count += item[field] || 0;
                    });
                    dataMap[item.day] = count;
                });
            }
            
            // Use selected month
            const daysInMonth = getDaysInMonth(selectedChartYear, selectedChartMonth);
            const monthName = monthNamesFull[selectedChartMonth];
            
            // Update chart title
            const titleEl = document.getElementById('chartTitle');
            if (titleEl) titleEl.textContent = 'Активность по дням';
            
            // Update month label (in header)
            const monthLabelEl = document.getElementById('chartMonthLabel');
            if (monthLabelEl) monthLabelEl.textContent = monthName + ' ' + selectedChartYear;
            
            // Generate all days of selected month (1 to last day)
            for (let day = 1; day <= daysInMonth; day++) {
                const d = new Date(selectedChartYear, selectedChartMonth, day);
                const dateStr = d.toISOString().split('T')[0]; // YYYY-MM-DD
                const fullLabel = d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long' }); // "29 марта"
                
                // Just show day number
                labels.push(String(day));
                chartFullDates.push(fullLabel);
                chartData.push(dataMap[dateStr] || 0);
            }
            
            profileActivityChartInstance = new Chart(canvas, {
                type: 'bar',
                data: {
                    labels: labels,
                    datasets: [{
                        label: config.label,
                        data: chartData,
                        backgroundColor: config.color,
                        borderRadius: 2,
                        barPercentage: 0.7,
                        categoryPercentage: 0.9
                    }]
                },
                options: {
                    responsive: true,
                    maintainAspectRatio: false,
                    interaction: {
                        mode: 'index',
                        intersect: false
                    },
                    plugins: { 
                        legend: { display: false },
                        tooltip: {
                            enabled: true,
                            mode: 'index',
                            intersect: false,
                            callbacks: {
                                title: function(context) {
                                    const idx = context[0].dataIndex;
                                    return chartFullDates[idx] || '';
                                },
                                label: function(context) {
                                    return config.label + ': ' + context.raw;
                                }
                            }
                        }
                    },
                    scales: { 
                        y: { beginAtZero: true, grid: { color: '#2a2a2a' }, ticks: { stepSize: 1 } }, 
                        x: { 
                            grid: { display: false },
                            ticks: {
                                maxRotation: 0,
                                minRotation: 0,
                                font: { size: 10 },
                                color: '#6c7293'
                            }
                        } 
                    }
                }
            });
        }
        
        function goBack() {
            document.getElementById('balancePage').classList.add('hidden');
            document.getElementById('adminPage').classList.add('hidden');
            const token = localStorage.getItem('token');
            if (token) {
                navigateTo('home');
            } else {
                showLanding();
            }
        }
        
        // Clear activity history (frontend only)
        function clearActivityHistory() {
            if (!confirm('Очистить историю активности?')) return;
            const tableBody = document.getElementById('profileActivityTable');
            if (tableBody) {
                tableBody.innerHTML = `
                    <tr>
                        <td colspan="4" style="text-align:center;color:#6c7293;padding:30px;">
                            <i class="fa-solid fa-clock-rotate-left" style="font-size:2rem;margin-bottom:10px;display:block;opacity:0.5;"></i>
                            История действий пуста
                        </td>
                    </tr>
                `;
            }
        }
        
        // New navigation functions for pages
        function showPage(page, skipToolLoad = false) {
            // Save current page
            localStorage.setItem('currentPage', page);
            
            // Hide all content pages
            document.getElementById('landingPage').classList.add('hidden');
            document.getElementById('dashboardPage').classList.add('hidden');
            document.getElementById('balancePage').classList.add('hidden');
            document.getElementById('adminPage').classList.add('hidden');
            document.getElementById('telegramPage').classList.add('hidden');
            document.getElementById('mtproxyPage').classList.add('hidden');
            
            // Show the selected page
            if (page === 'home') {
                const token = localStorage.getItem('token');
                if (token) {
                    document.getElementById('dashboardPage').classList.remove('hidden');
                } else {
                    document.getElementById('landingPage').classList.remove('hidden');
                }
            } else if (page === 'telegram') {
                document.getElementById('telegramPage').classList.remove('hidden');
                // Only load tool if not called from router (which will load it itself)
                if (!skipToolLoad) {
                    const savedTool = localStorage.getItem('currentTelegramTool') || 'warmup';
                    showTelegramTool(savedTool);
                }
            } else if (page === 'mtproxy') {
                document.getElementById('mtproxyPage').classList.remove('hidden');
                // Автоматическая проверка статуса прокси и загрузка списка
                checkProxyStatus();
                loadMTProxyList();
            }
            
            updateNavLinks(page);
        }
        
        function updateNavLinks(activePage) {
            document.querySelectorAll('.nav-link').forEach(link => {
                if (link.dataset.page === activePage) {
                    link.classList.add('active');
                } else {
                    link.classList.remove('active');
                }
            });
        }
        
        function showTelegramTool(tool, updateHash = true) {
            // Don't switch to disabled tools
            const clickedItem = document.querySelector(`.sidebar-item[data-tool="${tool}"]`);
            if (clickedItem && (clickedItem.classList.contains('disabled') || clickedItem.classList.contains('disabled-feature'))) {
                return;
            }
            
            // Overview не требует авторизации
            if (tool !== 'overview') {
                // Требуем авторизацию для использования инструментов
                if (!requireAuth()) {
                    // Сохраняем куда хотел перейти пользователь
                    localStorage.setItem('pendingRoute', tool);
                    return;
                }
                localStorage.setItem('currentTelegramTool', tool);
            }
            
            // Update URL (only if called directly, not from router)
            if (updateHash && window.location.pathname !== '/' + tool) {
                history.pushState(null, '', '/' + tool);
            }
            
            // Update sidebar active state
            document.querySelectorAll('.sidebar-item').forEach(item => {
                if (item.dataset.tool === tool) {
                    item.classList.add('active');
                } else {
                    item.classList.remove('active');
                }
            });
            
            // Show selected tool content
            document.querySelectorAll('.tool-content').forEach(content => {
                content.classList.add('hidden');
            });
            
            // Convert tool name to ID (e.g., 'add-account' -> 'toolAddAccount')
            const toolId = 'tool' + tool.split('-').map(part => part.charAt(0).toUpperCase() + part.slice(1)).join('');
            const toolContent = document.getElementById(toolId);
            if (toolContent) {
                toolContent.classList.remove('hidden');
            }
            
            // Load data when switching to a tool
            if (tool === 'projects') loadProjects();
            if (tool === 'proxies') loadProxies();
            if (tool === 'accounts') { scheduleAccountsLoad(); setTimeout(() => { if (typeof resumeBulkCheckIfNeeded === 'function') resumeBulkCheckIfNeeded(); }, 2500); }
            else {
                // Останавливаем polling аккаунтов при переходе в другой раздел
                if (accountsPollingInterval) {
                    clearInterval(accountsPollingInterval);
                    accountsPollingInterval = null;
                }
            }
            if (tool === 'warmup') {
                checkWarmupHealth();
                loadWarmupJobs();
                startWarmupJobsRefresh();
            }
            if (tool === 'files') {
                loadWarmupFiles();
            }
            if (tool === 'parsing') {
                loadParserJobs();
                startParserJobsRefresh();
            }
            if (tool === 'groups') {
                loadManagedGroups();
            }
            if (tool === 'chat-parser') {
                loadChatSearchJobs();
                loadAccountsForChatSearch();
            }
            if (tool === 'inviting') {
                loadInvitingJobs();
                // Автообновление списка задач каждые 8 секунд
                if (invitingListAutoRefresh) clearInterval(invitingListAutoRefresh);
                invitingListAutoRefresh = setInterval(loadInvitingJobs, 8000);
            } else {
                if (invitingListAutoRefresh) { clearInterval(invitingListAutoRefresh); invitingListAutoRefresh = null; }
            }
            if (tool === 'broadcast') {
                const bcFilter = document.getElementById('broadcastFilterStatus');
                if (bcFilter) bcFilter.value = '';
                loadBroadcastJobs();
                if (window.broadcastListAutoRefresh) clearInterval(window.broadcastListAutoRefresh);
                window.broadcastListAutoRefresh = setInterval(loadBroadcastJobs, 15000);
            } else {
                if (window.broadcastListAutoRefresh) { clearInterval(window.broadcastListAutoRefresh); window.broadcastListAutoRefresh = null; }
            }
            if (tool === 'monitoring') {
                loadMonitorTasks();
                loadAccountsForMonitor();
                startMonitorAutoRefresh();
            } else {
                if (monitorRefreshInterval) { clearInterval(monitorRefreshInterval); monitorRefreshInterval = null; }
            }
            if (tool === 'autoposter') {
                apLoadAccounts();
                switchAutoposterTab('tasks');
            }
        }
        
        // ============================================================================
        // Projects Manager Functions
        // ============================================================================
        
        let allProjects = [];
        
        async function loadProjects() {
            const token = localStorage.getItem('token');
            if (!token) return;
            const tbody = document.getElementById('projectsTableBody');
            tbody.innerHTML = '<tr><td colspan="5" style="padding:30px;text-align:center;color:var(--text-secondary);">Загрузка...</td></tr>';
            
            try {
                const res = await fetch(`${API_URL}/projects`, {
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                if (!res.ok) throw new Error('Ошибка загрузки');
                const data = await res.json();
                allProjects = data.projects || [];
                cachedProjects = allProjects; // Обновляем глобальный кэш
                renderProjectsTable();
            } catch (err) {
                tbody.innerHTML = `<tr><td colspan="5" style="padding:30px;text-align:center;color:#ff6b6b;">${escapeHtml(err.message)}</td></tr>`;
            }
        }
        
        function renderProjectsTable() {
            const tbody = document.getElementById('projectsTableBody');
            
            if (!allProjects.length) {
                tbody.innerHTML = '<tr><td colspan="5" style="padding:30px;text-align:center;color:var(--text-secondary);">Нет проектов. Создайте первый проект для группировки аккаунтов.</td></tr>';
                return;
            }
            
            tbody.innerHTML = allProjects.map(p => {
                const created = new Date(p.created_at).toLocaleDateString('ru-RU');
                return `
                <tr style="border-bottom:1px solid var(--border);">
                    <td style="padding:10px 14px;font-weight:500;">${p.name}</td>
                    <td style="padding:10px 14px;color:var(--text-secondary);font-size:0.85rem;">${p.description || '—'}</td>
                    <td style="padding:10px 14px;"><span style="background:var(--bg-hover);padding:3px 8px;border-radius:10px;font-size:0.8rem;">${p.accounts_count}</span></td>
                    <td style="padding:10px 14px;color:var(--text-secondary);font-size:0.8rem;">${created}</td>
                    <td style="padding:10px 14px;text-align:right;">
                        <button onclick="editProject(${p.id},'${p.name.replace(/'/g, "\\'")}','${(p.description || '').replace(/'/g, "\\'")}')" style="background:transparent;border:1px solid var(--border);border-radius:4px;color:var(--text-secondary);padding:4px 8px;cursor:pointer;margin-right:4px;font-size:.75rem;" title="Редактировать">✏️</button>
                        <button onclick="deleteProject(${p.id})" style="background:transparent;border:1px solid rgba(255,107,107,.4);border-radius:4px;color:#ff6b6b;padding:4px 8px;cursor:pointer;font-size:.75rem;" title="Удалить">🗑️</button>
                    </td>
                </tr>`;
            }).join('');
        }
        
        function showCreateProjectModal() {
            const modal = document.createElement('div');
            modal.className = 'modal-overlay show';
            modal.id = 'projectModal';
            modal.innerHTML = `
                <div class="modal" style="max-width:420px;">
                    <h2 style="margin-bottom:12px;font-size:1rem;">📁 Создать проект</h2>
                    <div class="input-group">
                        <label>Название проекта</label>
                        <input type="text" id="projectName" placeholder="Например: Россия" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                    </div>
                    <div class="input-group">
                        <label>Описание (необязательно)</label>
                        <input type="text" id="projectDesc" placeholder="Краткое описание" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                    </div>
                    <div id="projectError" class="error"></div>
                    <div class="modal-buttons">
                        <button class="btn btn-secondary" onclick="closeProjectModal()">Отмена</button>
                        <button id="createProjectBtn" class="btn" onclick="createProject()">Создать</button>
                    </div>
                </div>`;
            document.body.appendChild(modal);
            document.getElementById('projectName').focus();
        }
        
        function closeProjectModal() {
            const modal = document.getElementById('projectModal');
            if (modal) modal.remove();
        }
        
        // Создание проекта прямо из массовой загрузки
        function showMassUploadCreateProject() {
            const modal = document.createElement('div');
            modal.className = 'modal-overlay show';
            modal.id = 'massUploadProjectModal';
            modal.style.zIndex = '10001';
            modal.innerHTML = `
                <div class="modal" style="max-width:400px;">
                    <h2 style="margin-bottom:12px;font-size:1rem;">📁 Новый проект</h2>
                    <div class="input-group">
                        <label>Название</label>
                        <input type="text" id="massUploadNewProjectName" placeholder="Например: Германия" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                    </div>
                    <div id="massUploadProjectError" class="error" style="display:none;"></div>
                    <div class="modal-buttons">
                        <button class="btn btn-secondary" onclick="document.getElementById('massUploadProjectModal').remove()">Отмена</button>
                        <button id="massUploadCreateProjectBtn" class="btn" onclick="massUploadCreateProject()">Создать</button>
                    </div>
                </div>`;
            document.body.appendChild(modal);
            document.getElementById('massUploadNewProjectName').focus();
        }
        
        async function massUploadCreateProject() {
            const name = document.getElementById('massUploadNewProjectName').value.trim();
            const errorEl = document.getElementById('massUploadProjectError');
            const btn = document.getElementById('massUploadCreateProjectBtn');
            if (!name) { errorEl.textContent = 'Введите название'; errorEl.style.display = 'block'; return; }
            btn.disabled = true; btn.innerHTML = '<span class="spinner"></span>...';
            try {
                const res = await fetch(`${API_URL}/projects`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('token')}` },
                    body: JSON.stringify({ name, description: null })
                });
                if (!res.ok) { const d = await res.json(); throw new Error(d.detail || 'Ошибка'); }
                const data = await res.json();
                const created = data.project;
                // Добавляем в глобальный кэш и в select
                allProjects.push({ id: created.id, name: created.name });
                const sel = document.getElementById('massUploadProject');
                const opt = document.createElement('option');
                opt.value = created.id; opt.textContent = created.name;
                sel.appendChild(opt);
                sel.value = created.id;
                document.getElementById('massUploadProjectModal').remove();
            } catch (err) {
                errorEl.textContent = err.message; errorEl.style.display = 'block';
                btn.disabled = false; btn.textContent = 'Создать';
            }
        }
        
        async function createProject() {
            const name = document.getElementById('projectName').value.trim();
            const description = document.getElementById('projectDesc').value.trim();
            const errorEl = document.getElementById('projectError');
            const btn = document.getElementById('createProjectBtn');
            
            if (!name) {
                errorEl.textContent = 'Введите название проекта';
                errorEl.style.display = 'block';
                return;
            }
            
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner"></span>Создание...';
            
            try {
                const res = await fetch(`${API_URL}/projects`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${localStorage.getItem('token')}`
                    },
                    body: JSON.stringify({ name, description: description || null })
                });
                
                if (!res.ok) {
                    const data = await res.json();
                    throw new Error(data.detail || 'Ошибка создания');
                }
                
                closeProjectModal();
                invalidateDataCache(); // Обновить кэш проектов
                loadProjects();
            } catch (err) {
                errorEl.textContent = err.message;
                errorEl.style.display = 'block';
                btn.disabled = false;
                btn.textContent = 'Создать';
            }
        }
        
        function editProject(id, name, desc) {
            const modal = document.createElement('div');
            modal.className = 'modal-overlay show';
            modal.id = 'projectModal';
            modal.innerHTML = `
                <div class="modal" style="max-width:420px;">
                    <h2 style="margin-bottom:12px;font-size:1rem;">✏️ Редактировать проект</h2>
                    <div class="input-group">
                        <label>Название проекта</label>
                        <input type="text" id="projectName" value="${name}" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                    </div>
                    <div class="input-group">
                        <label>Описание</label>
                        <input type="text" id="projectDesc" value="${desc}" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                    </div>
                    <div id="projectError" class="error"></div>
                    <div class="modal-buttons">
                        <button class="btn btn-secondary" onclick="closeProjectModal()">Отмена</button>
                        <button id="updateProjectBtn" class="btn" onclick="updateProject(${id})">Сохранить</button>
                    </div>
                </div>`;
            document.body.appendChild(modal);
        }
        
        async function updateProject(id) {
            const name = document.getElementById('projectName').value.trim();
            const description = document.getElementById('projectDesc').value.trim();
            const errorEl = document.getElementById('projectError');
            const btn = document.getElementById('updateProjectBtn');
            
            if (!name) {
                errorEl.textContent = 'Введите название проекта';
                errorEl.style.display = 'block';
                return;
            }
            
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner"></span>Сохранение...';
            
            try {
                const res = await fetch(`${API_URL}/projects/${id}`, {
                    method: 'PUT',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${localStorage.getItem('token')}`
                    },
                    body: JSON.stringify({ name, description: description || null })
                });
                
                if (!res.ok) {
                    const data = await res.json();
                    throw new Error(data.detail || 'Ошибка обновления');
                }
                
                closeProjectModal();
                loadProjects();
            } catch (err) {
                errorEl.textContent = err.message;
                errorEl.style.display = 'block';
                btn.disabled = false;
                btn.textContent = 'Сохранить';
            }
        }
        
        async function deleteProject(id) {
            if (!await ugConfirm('Удалить проект? Аккаунты не будут удалены, только отвязаны от проекта.')) return;
            
            try {
                const res = await fetch(`${API_URL}/projects/${id}`, {
                    method: 'DELETE',
                    headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` }
                });
                if (!res.ok) throw new Error('Ошибка удаления');
                invalidateDataCache(); // Обновить кэш проектов
                loadProjects();
            } catch (err) {
                showToast('Ошибка: ' + err.message, 'error');
            }
        }
        
        // ============================================================================
        // Global Data Cache (proxies, projects) - загружается один раз
        // ============================================================================
        
        let cachedProxies = [];
        let cachedProxyGroups = [];
        let cachedProjects = [];
        let dataCacheLoaded = false;
        let dataCacheLoading = false;
        
        async function ensureDataCacheLoaded() {
            if (dataCacheLoaded) return;
            if (dataCacheLoading) {
                // Ждём пока другой вызов загрузит
                while (dataCacheLoading) await new Promise(r => setTimeout(r, 50));
                return;
            }
            dataCacheLoading = true;
            const token = localStorage.getItem('token');
            if (!token) { dataCacheLoading = false; return; }
            
            try {
                const [proxiesRes, groupsRes, projectsRes] = await Promise.all([
                    fetch(`${API_URL}/proxies`, { headers: { 'Authorization': `Bearer ${token}` }}),
                    fetch(`${API_URL}/proxy-groups`, { headers: { 'Authorization': `Bearer ${token}` }}),
                    fetch(`${API_URL}/projects`, { headers: { 'Authorization': `Bearer ${token}` }})
                ]);
                
                if (proxiesRes.ok) {
                    const data = await proxiesRes.json();
                    cachedProxies = data.proxies || [];
                }
                if (groupsRes.ok) {
                    const data = await groupsRes.json();
                    cachedProxyGroups = data.groups || [];
                }
                if (projectsRes.ok) {
                    const data = await projectsRes.json();
                    cachedProjects = data.projects || [];
                }
                dataCacheLoaded = true;
            } catch (e) {
                console.error('Cache load error:', e);
            } finally {
                dataCacheLoading = false;
            }
        }
        
        function invalidateDataCache() {
            dataCacheLoaded = false;
        }
        
        // ============================================================================
        // Proxy Manager Functions
        // ============================================================================
        
        let expandedGroups = new Set();
        let checkingGroups = new Set();
        let checkingProxies = new Set();
        let checkAbortControllers = new Map();
        let proxyAbortControllers = new Map();
        // task_id → groupId map для серверного polling
        let proxyCheckTasks = new Map();
        let checkingPollingInterval = null;

        // Восстанавливаем состояние проверки из localStorage (task_id based)
        function restoreCheckingState() {
            try {
                const saved = localStorage.getItem('proxyCheckTasks');
                if (saved) {
                    const data = JSON.parse(saved);
                    const now = Date.now();
                    Object.entries(data).forEach(([taskId, info]) => {
                        // Восстанавливаем только проверки младше 15 минут
                        if (now - info.startTime < 15 * 60 * 1000) {
                            proxyCheckTasks.set(taskId, info);
                            checkingGroups.add(info.groupId);
                        }
                    });
                    if (checkingGroups.size > 0) {
                        startCheckingPolling();
                    }
                }
            } catch (e) {}
        }

        function saveCheckingState() {
            const data = {};
            proxyCheckTasks.forEach((info, taskId) => {
                data[taskId] = info;
            });
            if (Object.keys(data).length > 0) {
                localStorage.setItem('proxyCheckTasks', JSON.stringify(data));
            } else {
                localStorage.removeItem('proxyCheckTasks');
            }
            // Очищаем старый формат
            localStorage.removeItem('checkingGroups');
        }

        function startCheckingPolling() {
            if (checkingPollingInterval) return;
            checkingPollingInterval = setInterval(async () => {
                if (proxyCheckTasks.size === 0) {
                    clearInterval(checkingPollingInterval);
                    checkingPollingInterval = null;
                    return;
                }

                const token = localStorage.getItem('token');
                const now = Date.now();
                let anyCompleted = false;

                for (const [taskId, info] of [...proxyCheckTasks.entries()]) {
                    // Таймаут 15 минут
                    if (now - info.startTime > 15 * 60 * 1000) {
                        proxyCheckTasks.delete(taskId);
                        checkingGroups.delete(info.groupId);
                        anyCompleted = true;
                        continue;
                    }

                    try {
                        const res = await fetch(`${API_URL}/proxy-check-status/${taskId}`, {
                            headers: { 'Authorization': `Bearer ${token}` }
                        });
                        if (!res.ok) continue;
                        const data = await res.json();

                        if (data.status === 'completed') {
                            proxyCheckTasks.delete(taskId);
                            checkingGroups.delete(info.groupId);
                            anyCompleted = true;
                            const r = data.data;
                            if (r.deleted > 0) {
                                showToast(`Группа проверена: ${r.working} рабочих, удалено ${r.deleted}`, 'success');
                            } else {
                                showToast(`Группа проверена: ${r.working}/${r.checked} рабочих`, 'success');
                            }
                        } else if (data.status === 'failed') {
                            proxyCheckTasks.delete(taskId);
                            checkingGroups.delete(info.groupId);
                            anyCompleted = true;
                            showToast('Ошибка проверки прокси', 'error');
                        }
                        // progress / pending — продолжаем ждать
                    } catch (e) {}
                }

                saveCheckingState();
                if (anyCompleted) {
                    await loadProxies();
                } else {
                    renderProxyGroups();
                }

                if (proxyCheckTasks.size === 0) {
                    clearInterval(checkingPollingInterval);
                    checkingPollingInterval = null;
                }
            }, 3000);
        }
        const spinnerSmall = '<span style="display:inline-block;width:14px;height:14px;border:2px solid rgba(255,165,0,.3);border-radius:50%;border-top-color:#ff9800;animation:spin 0.8s linear infinite;"></span>';
        const spinnerTiny = '<span style="display:inline-block;width:12px;height:12px;border:2px solid rgba(255,165,0,.3);border-radius:50%;border-top-color:#ff9800;animation:spin 0.8s linear infinite;"></span>';
        
        let checkingStateRestored = false;
        
        async function loadProxies() {
            const token = localStorage.getItem('token');
            if (!token) return;
            
            // Восстанавливаем состояние проверки при первой загрузке
            if (!checkingStateRestored) {
                checkingStateRestored = true;
                restoreCheckingState();
            }
            
            const container = document.getElementById('proxyGroupsContainer');
            const isRefresh = checkingGroups.size > 0 || checkingProxies.size > 0;
            if (!isRefresh) container.innerHTML = getInlineLoaderHtml();
            
            try {
                // Загружаем группы и прокси параллельно
                const [groupsRes, proxiesRes] = await Promise.all([
                    fetch(`${API_URL}/proxy-groups`, { headers: { 'Authorization': `Bearer ${token}` } }),
                    fetch(`${API_URL}/proxies`, { headers: { 'Authorization': `Bearer ${token}` } })
                ]);
                
                if (!groupsRes.ok || !proxiesRes.ok) throw new Error('Ошибка загрузки');
                
                const groupsData = await groupsRes.json();
                const proxiesData = await proxiesRes.json();
                
                cachedProxyGroups = groupsData.groups || [];
                cachedProxies = proxiesData.proxies || [];
                
                renderProxyGroups();
            } catch (err) {
                container.innerHTML = `<div style="padding:40px;text-align:center;color:#ff6b6b;">${escapeHtml(err.message)}</div>`;
            }
        }
        
        function renderProxyGroups() {
            const container = document.getElementById('proxyGroupsContainer');
            
            // Считаем прокси без группы
            const ungroupedProxies = cachedProxies.filter(p => !p.group_id);
            
            if (!cachedProxyGroups.length && !ungroupedProxies.length) {
                container.innerHTML = '<div style="padding:40px;text-align:center;color:var(--text-secondary);background:var(--bg-card);border-radius:12px;">Нет прокси. Нажмите «Массовая загрузка» чтобы добавить.</div>';
                return;
            }
            
            let html = '';
            
            // Рендерим группы
            cachedProxyGroups.forEach(group => {
                const isExpanded = expandedGroups.has(group.id);
                const groupProxies = cachedProxies.filter(p => p.group_id === group.id);
                const activeCount = groupProxies.filter(p => p.is_active).length;
                
                // Определяем флаг страны по первому прокси в группе
                const firstProxy = groupProxies[0];
                const countryFlag = firstProxy && firstProxy.country_code ? countryCodeToFlag(firstProxy.country_code) : '📁';
                const countryName = firstProxy && firstProxy.country ? firstProxy.country : '';
                const hasCountry = firstProxy && firstProxy.country_code;
                
                html += `
                <div class="proxy-group-card" style="background:var(--bg-card);border:1px solid var(--border);border-radius:12px;overflow:hidden;">
                    <div onclick="toggleProxyGroup(${group.id})" style="padding:16px 20px;cursor:pointer;display:flex;justify-content:space-between;align-items:center;background:${isExpanded ? 'var(--bg-hover)' : 'transparent'};transition:background 0.2s;">
                        <div style="display:flex;align-items:center;gap:12px;flex:1;min-width:0;">
                            <span style="font-size:1.2rem;transition:transform 0.2s;transform:rotate(${isExpanded ? '90deg' : '0'});flex-shrink:0;">▶</span>
                            <div style="min-width:0;">
                                <div style="font-weight:600;font-size:1rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${countryFlag} ${group.name}</div>
                                <div style="font-size:0.8rem;color:var(--text-secondary);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${countryName ? countryName + ' • ' : ''}${group.description || ''}</div>
                            </div>
                        </div>
                        <div style="display:flex;align-items:center;gap:8px;flex-shrink:0;">
                            <span style="background:rgba(245,158,11,.15);color:#f59e0b;padding:4px 0;border-radius:6px;font-size:0.8rem;width:95px;text-align:center;display:inline-block;">✓ ${activeCount} рабочих</span>
                            <span style="background:var(--bg-hover);padding:4px 0;border-radius:6px;font-size:0.85rem;width:50px;text-align:center;display:inline-block;">🌐 ${groupProxies.length}</span>
                            <span style="background:var(--bg-hover);padding:4px 0;border-radius:6px;font-size:0.85rem;width:55px;text-align:center;display:inline-block;">👥 ${group.accounts_count || 0}</span>
                            <button onclick="event.stopPropagation();showProxyModal(null,${group.id})" style="background:rgba(245,158,11,.15);border:1px solid rgba(245,158,11,.4);border-radius:6px;color:#f59e0b;padding:6px 12px;cursor:pointer;font-size:0.8rem;" title="Добавить прокси">➕</button>
                            <button onclick="event.stopPropagation();checkGroupProxies(${group.id})" style="background:rgba(91,192,222,.15);border:1px solid rgba(91,192,222,.4);border-radius:6px;color:#5bc0de;padding:6px 12px;cursor:pointer;font-size:0.8rem;" title="Проверить прокси">${checkingGroups.has(group.id) ? spinnerSmall : '🔄'}</button>
                            <button onclick="event.stopPropagation();editProxyGroup(${group.id},'${group.name.replace(/'/g, "\\'")}','${(group.description || '').replace(/'/g, "\\'")}')" style="background:var(--bg-hover);border:1px solid var(--border);border-radius:6px;color:var(--text-primary);padding:6px 12px;cursor:pointer;font-size:0.8rem;" title="Редактировать">✏️</button>
                            <button onclick="event.stopPropagation();clearGroupProxies(${group.id},'${group.name.replace(/'/g, "\\'")}',${ groupProxies.length})" style="background:rgba(249,115,22,.15);border:1px solid rgba(249,115,22,.4);border-radius:6px;color:#f97316;padding:6px 12px;cursor:pointer;font-size:0.8rem;" title="Очистить все прокси">⚠️</button>
                            <button onclick="event.stopPropagation();deleteProxyGroup(${group.id},'${group.name.replace(/'/g, "\\'")}')" style="background:rgba(255,107,107,.15);border:1px solid rgba(255,107,107,.4);border-radius:6px;color:#ff6b6b;padding:6px 12px;cursor:pointer;font-size:0.8rem;" title="Удалить">🗑️</button>
                        </div>
                    </div>
                    ${isExpanded ? renderProxyList(groupProxies, group.id) : ''}
                </div>`;
            });
            
            // Прокси без группы — показываем только если есть, с предложением удалить
            if (ungroupedProxies.length > 0) {
                html += `
                <div style="background:rgba(255,107,107,0.1);border:1px solid rgba(255,107,107,0.3);border-radius:12px;padding:16px 20px;display:flex;justify-content:space-between;align-items:center;">
                    <div>
                        <div style="font-weight:600;color:#ff6b6b;">⚠️ Найдено ${ungroupedProxies.length} прокси без группы</div>
                        <div style="font-size:0.8rem;color:var(--text-secondary);margin-top:4px;">Это мусор от удалённых групп. Рекомендуем удалить.</div>
                    </div>
                    <button onclick="deleteUngroupedProxies()" style="padding:8px 16px;background:#ff6b6b;border:none;border-radius:6px;color:#fff;font-size:0.85rem;cursor:pointer;font-weight:600;">🗑️ Удалить все</button>
                </div>`;
            }
            
            container.innerHTML = html;
        }
        
        function renderProxyList(proxies, groupId) {
            if (!proxies.length) return '<div style="padding:20px;text-align:center;color:var(--text-secondary);font-size:0.85rem;">Нет прокси в этой группе</div>';
            
            return `
            <div style="border-top:1px solid var(--border);max-height:400px;overflow-y:auto;">
                <table style="width:100%;border-collapse:collapse;">
                    <thead>
                        <tr style="background:var(--bg-hover);">
                            <th style="padding:10px 16px;text-align:left;font-size:.75rem;color:var(--text-secondary);font-weight:600;">Страна</th>
                            <th style="padding:10px 16px;text-align:left;font-size:.75rem;color:var(--text-secondary);font-weight:600;">Прокси</th>
                            <th style="padding:10px 16px;text-align:left;font-size:.75rem;color:var(--text-secondary);font-weight:600;">Тип</th>
                            <th style="padding:10px 16px;text-align:left;font-size:.75rem;color:var(--text-secondary);font-weight:600;">Статус</th>
                            <th style="padding:10px 16px;text-align:left;font-size:.75rem;color:var(--text-secondary);font-weight:600;">Акк.</th>
                            <th style="padding:10px 16px;text-align:right;font-size:.75rem;color:var(--text-secondary);font-weight:600;">Действия</th>
                        </tr>
                    </thead>
                    <tbody>
                        ${proxies.map(p => {
                            const statusStyle = p.is_active 
                                ? 'background:rgba(245,158,11,.2);color:#f59e0b;' 
                                : 'background:rgba(255,193,7,.2);color:#ffc107;';
                            const statusText = p.is_active ? '✓' : '?';
                            const flag = p.country_code ? countryCodeToFlag(p.country_code) : '🌍';
                            const countryName = p.country || '';
                            const proxyType = (p.proxy_type || 'socks5').toUpperCase();
                            const typeColor = proxyType.includes('SOCKS') ? 'color:#5bc0de;background:rgba(91,192,222,.15);' : 'color:#ff9800;background:rgba(255,152,0,.15);';
                            return `
                            <tr style="border-bottom:1px solid var(--border);">
                                <td style="padding:10px 16px;font-size:0.9rem;" title="${countryName}">${flag} ${countryName}</td>
                                <td style="padding:10px 16px;font-family:monospace;font-size:.8rem;">${p.host}:${p.port}</td>
                                <td style="padding:10px 16px;"><span style="display:inline-block;padding:2px 8px;border-radius:10px;font-size:.65rem;font-weight:600;${typeColor}">${proxyType}</span></td>
                                <td style="padding:10px 16px;"><span style="display:inline-block;padding:2px 8px;border-radius:10px;font-size:.7rem;${statusStyle}">${statusText}</span></td>
                                <td style="padding:10px 16px;font-size:0.85rem;">${p.accounts_count}</td>
                                <td style="padding:10px 16px;text-align:right;">
                                    <button onclick="event.stopPropagation();checkProxy(this,${p.id})" style="background:transparent;border:1px solid var(--border);border-radius:4px;color:var(--text-secondary);padding:4px 8px;cursor:pointer;font-size:.75rem;margin-right:2px;" title="Проверить">${(checkingGroups.has(groupId) || checkingProxies.has(p.id)) ? spinnerTiny : '🔄'}</button>
                                    <button onclick="event.stopPropagation();editProxy(${p.id})" style="background:transparent;border:1px solid var(--border);border-radius:4px;color:var(--text-secondary);padding:4px 8px;cursor:pointer;font-size:.75rem;margin-right:2px;">✏️</button>
                                    <button onclick="event.stopPropagation();deleteProxy(${p.id})" style="background:transparent;border:1px solid rgba(255,107,107,.3);border-radius:4px;color:#ff6b6b;padding:4px 8px;cursor:pointer;font-size:.75rem;">🗑️</button>
                                </td>
                            </tr>`;
                        }).join('')}
                    </tbody>
                </table>
            </div>`;
        }
        
        function toggleProxyGroup(groupId) {
            if (expandedGroups.has(groupId)) {
                expandedGroups.delete(groupId);
            } else {
                expandedGroups.add(groupId);
            }
            renderProxyGroups();
        }
        
        // Создание группы
        function showCreateGroupModal() {
            hideAllModals();
            document.getElementById('newGroupName').value = '';
            document.getElementById('newGroupDescription').value = '';
            document.getElementById('createGroupError').style.display = 'none';
            document.getElementById('createGroupModal').classList.add('show');
        }
        
        async function createProxyGroup() {
            const token = localStorage.getItem('token');
            const name = document.getElementById('newGroupName').value.trim();
            const description = document.getElementById('newGroupDescription').value.trim();
            
            if (!name) {
                showError('createGroupError', 'Введите название группы');
                return;
            }
            
            try {
                const res = await fetch(`${API_URL}/proxy-groups`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify({ name, description: description || null })
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка');
                
                hideAllModals();
                loadProxies();
            } catch (err) {
                showError('createGroupError', err.message);
            }
        }
        
        let pendingDeleteGroupId = null;
        let pendingDeleteUngrouped = false;
        
        function deleteProxyGroup(groupId, groupName) {
            pendingDeleteGroupId = groupId;
            pendingDeleteUngrouped = false;
            document.getElementById('confirmDeleteGroupText').textContent = `Удалить группу "${groupName}" вместе со всеми прокси?`;
            document.getElementById('confirmDeleteGroupBtn').textContent = 'Удалить';
            hideAllModals();
            document.getElementById('confirmDeleteGroupModal').classList.add('show');
        }
        
        window._pendingClearGroupId = null;
        
        function clearGroupProxies(groupId, groupName, count) {
            window._pendingClearGroupId = groupId;
            pendingDeleteGroupId = null;
            pendingDeleteUngrouped = false;
            document.getElementById('confirmDeleteGroupText').textContent = `Удалить все ${count} прокси из группы "${groupName}"? Группа останется.`;
            document.getElementById('confirmDeleteGroupBtn').textContent = 'Очистить';
            hideAllModals();
            document.getElementById('confirmDeleteGroupModal').classList.add('show');
        }
        
        function deleteUngroupedProxies() {
            pendingDeleteGroupId = null;
            pendingDeleteUngrouped = true;
            document.getElementById('confirmDeleteGroupText').textContent = 'Удалить все прокси без группы?';
            document.getElementById('confirmDeleteGroupBtn').textContent = 'Удалить все';
            hideAllModals();
            document.getElementById('confirmDeleteGroupModal').classList.add('show');
        }
        
        async function confirmDeleteGroupAction() {
            hideAllModals();
            if (window._pendingClearGroupId) {
                await doClearGroupProxies(window._pendingClearGroupId);
                window._pendingClearGroupId = null;
            } else if (pendingDeleteUngrouped) {
                await doDeleteUngroupedProxies();
            } else if (pendingDeleteGroupId) {
                await doDeleteProxyGroup(pendingDeleteGroupId);
            }
            pendingDeleteGroupId = null;
            pendingDeleteUngrouped = false;
        }
        
        async function doClearGroupProxies(groupId) {
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(`${API_URL}/proxy-groups/${groupId}/proxies`, {
                    method: 'DELETE',
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                if (!res.ok) throw new Error('Ошибка очистки');
                const data = await res.json();
                showToast(data.message || 'Прокси удалены', 'success');
                loadProxies();
            } catch (err) {
                showToast('Ошибка: ' + err.message, 'error');
            }
        }
        
        async function doDeleteProxyGroup(groupId) {
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(`${API_URL}/proxy-groups/${groupId}`, {
                    method: 'DELETE',
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                if (!res.ok) throw new Error('Ошибка удаления');
                loadProxies();
            } catch (err) {
                showToast('Ошибка: ' + err.message, 'error');
            }
        }
        
        async function doDeleteUngroupedProxies() {
            
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(`${API_URL}/proxy-cleanup/ungrouped`, {
                    method: 'DELETE',
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                if (!res.ok) {
                    const err = await res.json();
                    throw new Error(err.detail || 'Ошибка удаления');
                }
                const data = await res.json();
                showToast(data.message, 'success');
                loadProxies();
            } catch (err) {
                showToast('Ошибка: ' + err.message, 'error');
            }
        }
        
        function editProxyGroup(groupId, name, description) {
            hideAllModals();
            document.getElementById('editGroupId').value = groupId;
            document.getElementById('editGroupName').value = name;
            document.getElementById('editGroupDescription').value = description || '';
            document.getElementById('editGroupError').style.display = 'none';
            document.getElementById('editGroupModal').classList.add('show');
        }
        
        async function saveEditGroup() {
            const token = localStorage.getItem('token');
            const groupId = document.getElementById('editGroupId').value;
            const name = document.getElementById('editGroupName').value.trim();
            const description = document.getElementById('editGroupDescription').value.trim();
            
            if (!name) {
                showError('editGroupError', 'Введите название группы');
                return;
            }
            
            try {
                const res = await fetch(`${API_URL}/proxy-groups/${groupId}`, {
                    method: 'PUT',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify({ name, description: description || null })
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка');
                
                hideAllModals();
                loadProxies();
            } catch (err) {
                showError('editGroupError', err.message);
            }
        }
        
        async function checkGroupProxies(groupId) {
            // Toggle: если уже проверяется — отменяем через Celery revoke
            if (checkingGroups.has(groupId)) {
                // Найти task_id для этой группы и отменить
                for (const [taskId, info] of proxyCheckTasks.entries()) {
                    if (info.groupId === groupId) {
                        try {
                            const token = localStorage.getItem('token');
                            await fetch(`${API_URL}/proxy-check-cancel/${taskId}`, {
                                method: 'POST',
                                headers: { 'Authorization': `Bearer ${token}` }
                            });
                        } catch(e) {}
                        proxyCheckTasks.delete(taskId);
                        break;
                    }
                }
                checkingGroups.delete(groupId);
                saveCheckingState();
                renderProxyGroups();
                return;
            }
            
            const token = localStorage.getItem('token');
            checkingGroups.add(groupId);
            renderProxyGroups();
            
            try {
                const res = await fetch(`${API_URL}/proxy-groups/${groupId}/check`, {
                    method: 'POST',
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка проверки');
                
                if (data.task_id) {
                    proxyCheckTasks.set(data.task_id, { groupId, startTime: Date.now(), deleteFailed: false });
                    saveCheckingState();
                    startCheckingPolling();
                } else {
                    // Пустая группа — сразу завершаем
                    checkingGroups.delete(groupId);
                    loadProxies();
                }
            } catch (err) {
                checkingGroups.delete(groupId);
                showToast('Ошибка: ' + err.message, 'error');
                renderProxyGroups();
            }
        }
        
        async function checkGroupProxiesWithDelete(groupId, deleteFailed) {
            if (checkingGroups.has(groupId)) return;
            
            const token = localStorage.getItem('token');
            checkingGroups.add(groupId);
            renderProxyGroups();
            
            try {
                const url = `${API_URL}/proxy-groups/${groupId}/check${deleteFailed ? '?delete_failed=true' : ''}`;
                const res = await fetch(url, {
                    method: 'POST',
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка проверки');
                
                if (data.task_id) {
                    proxyCheckTasks.set(data.task_id, { groupId, startTime: Date.now(), deleteFailed: !!deleteFailed });
                    saveCheckingState();
                    startCheckingPolling();
                } else {
                    checkingGroups.delete(groupId);
                    loadProxies();
                }
            } catch (err) {
                checkingGroups.delete(groupId);
                showToast('Ошибка: ' + err.message, 'error');
                renderProxyGroups();
            }
        }
        
        async function checkProxy(btn, id) {
            // Toggle: если уже проверяется — отменяем
            if (checkingProxies.has(id)) {
                const ctrl = proxyAbortControllers.get(id);
                if (ctrl) ctrl.abort();
                return;
            }
            
            const token = localStorage.getItem('token');
            const abortCtrl = new AbortController();
            proxyAbortControllers.set(id, abortCtrl);
            checkingProxies.add(id);
            renderProxyGroups();
            
            try {
                const res = await fetch(`${API_URL}/proxies/${id}/check`, {
                    method: 'POST',
                    headers: { 'Authorization': `Bearer ${token}` },
                    signal: abortCtrl.signal
                });
                const data = await res.json();
            } catch (err) {
                if (err.name !== 'AbortError') showToast('Ошибка проверки: ' + err.message, 'error');
            } finally {
                checkingProxies.delete(id);
                proxyAbortControllers.delete(id);
                if (checkingGroups.size === 0 && checkingProxies.size === 0) {
                    loadProxies();
                } else {
                    renderProxyGroups();
                }
            }
        }
        
        function showProxyModal(editId, groupId) {
            hideAllModals();
            const setVal = (elId, val) => { const el = document.getElementById(elId); if (el) el.value = val; };
            const setStyle = (elId, prop, val) => { const el = document.getElementById(elId); if (el) el.style[prop] = val; };
            setVal('proxyEditId', editId || '');
            setVal('proxyGroupId', groupId || '');
            const titleEl = document.getElementById('proxyModalTitle');
            if (titleEl) titleEl.textContent = editId ? 'Изменить прокси' : 'Добавить прокси';
            setVal('proxyQuickPaste', '');
            setVal('proxyName', '');
            setVal('proxyHost', '');
            setVal('proxyPort', '');
            setVal('proxyUsername', '');
            setVal('proxyPassword', '');
            setVal('proxyType', 'socks5');
            setVal('proxyNewGroupName', '');
            setStyle('proxyError', 'display', 'none');
            setStyle('proxyCountryPreview', 'display', 'none');
            setStyle('proxyNewGroupWrap', 'display', 'none');
            
            // Показываем селект группы если группа НЕ предустановлена (включая редактирование "мусора")
            const groupSelectWrap = document.getElementById('proxyGroupSelectWrap');
            if (!groupId) {
                groupSelectWrap.style.display = 'block';
                // Загружаем группы асинхронно
                loadProxyGroupsForSelect();
            } else {
                groupSelectWrap.style.display = 'none';
            }
            
            const modal = document.getElementById('proxyModal');
            if (modal) modal.classList.add('show');
        }
        
        async function loadProxyGroupsForSelect() {
            const token = localStorage.getItem('token');
            const select = document.getElementById('proxyGroupSelect');
            select.innerHTML = '<option value="">— Выберите группу —</option><option value="new">➕ Создать новую группу...</option>';
            try {
                const res = await fetch(`${API_URL}/proxy-groups`, { headers: { 'Authorization': `Bearer ${token}` } });
                if (res.ok) {
                    const data = await res.json();
                    (data.groups || []).forEach(g => {
                        select.innerHTML += `<option value="${g.id}">📁 ${g.name}</option>`;
                    });
                }
            } catch (err) {}
        }
        
        function onProxyGroupSelectChange() {
            const select = document.getElementById('proxyGroupSelect');
            const newGroupWrap = document.getElementById('proxyNewGroupWrap');
            if (select.value === 'new') {
                newGroupWrap.style.display = 'block';
                document.getElementById('proxyNewGroupName').focus();
            } else {
                newGroupWrap.style.display = 'none';
            }
        }
        
        // Парсинг строки прокси: host:port@login:password или host:port:login:password
        function parseProxyString(str) {
            str = str.trim();
            if (!str) return;
            
            let host, port, login, password;
            
            // Формат: host:port@login:password
            if (str.includes('@')) {
                const [hostPart, authPart] = str.split('@');
                if (hostPart.includes(':')) {
                    [host, port] = hostPart.split(':');
                }
                if (authPart && authPart.includes(':')) {
                    [login, password] = authPart.split(':');
                }
            } 
            // Формат: host:port:login:password
            else {
                const parts = str.split(':');
                if (parts.length >= 2) {
                    host = parts[0];
                    port = parts[1];
                    if (parts.length >= 4) {
                        login = parts[2];
                        password = parts[3];
                    }
                }
            }
            
            if (host) document.getElementById('proxyHost').value = host;
            if (port) document.getElementById('proxyPort').value = port;
            if (login) document.getElementById('proxyUsername').value = login;
            if (password) document.getElementById('proxyPassword').value = password;
        }
        
        async function editProxy(id) {
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(`${API_URL}/proxies/${id}`, {
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                if (!res.ok) throw new Error('Не найден');
                const p = await res.json();
                showProxyModal(id, p.group_id || null);
                
                const setVal = (elId, val) => { const el = document.getElementById(elId); if (el) el.value = val; };
                setVal('proxyName', p.name || '');
                setVal('proxyHost', p.host || '');
                setVal('proxyPort', p.port || '');
                setVal('proxyUsername', p.username || '');
                setVal('proxyPassword', p.password || '');
                setVal('proxyType', p.proxy_type || 'socks5');
                // Показываем страну если есть
                if (p.country || p.country_code) {
                    const preview = document.getElementById('proxyCountryPreview');
                    const flagEl = document.getElementById('proxyCountryFlag');
                    const nameEl = document.getElementById('proxyCountryName');
                    if (preview) preview.style.display = 'block';
                    if (flagEl) flagEl.innerHTML = p.country_code ? countryCodeToFlag(p.country_code) : '🌍';
                    if (nameEl) nameEl.textContent = p.country || 'Неизвестно';
                }
            } catch (err) {
                showToast('Ошибка: ' + err.message, 'error');
            }
        }
        
        // Конвертация кода страны в флаг (картинка через flagcdn.com)
        function countryCodeToFlag(code) {
            if (!code || code.length !== 2) return '<span style="font-size:1.2em;">🌍</span>';
            const lowerCode = code.toLowerCase();
            return `<img src="https://flagcdn.com/24x18/${lowerCode}.png" alt="${code}" style="vertical-align:middle;margin-right:4px;" onerror="this.outerHTML='🌍'">`;
        }
        
        // ============================================================================
        // Bulk Proxy Upload Functions
        // ============================================================================
        
        async function showBulkProxyModal(preselectedGroupId) {
            hideAllModals();
            document.getElementById('bulkProxyFile').value = '';
            document.getElementById('bulkProxyText').value = '';
            document.getElementById('bulkProxyType').value = 'socks5';
            document.getElementById('bulkProxyNamePrefix').value = '';
            document.getElementById('bulkProxyPreview').style.display = 'none';
            document.getElementById('bulkProxyError').style.display = 'none';
            document.getElementById('bulkProxyResult').style.display = 'none';
            document.getElementById('newGroupNameWrap').style.display = 'none';
            document.getElementById('bulkProxyNewGroup').value = '';
            document.getElementById('selectedFileName').style.display = 'none';
            
            // Загружаем группы в dropdown
            const token = localStorage.getItem('token');
            const groupSelect = document.getElementById('bulkProxyGroup');
            groupSelect.innerHTML = '<option value="">— Выберите группу —</option><option value="new">➕ Создать новую группу...</option>';
            
            try {
                const res = await fetch(`${API_URL}/proxy-groups`, { headers: { 'Authorization': `Bearer ${token}` } });
                if (res.ok) {
                    const data = await res.json();
                    (data.groups || []).forEach(g => {
                        groupSelect.innerHTML += `<option value="${g.id}">📁 ${g.name} (${g.proxies_count} прокси)</option>`;
                    });
                }
            } catch (err) {}
            
            // Handler для показа/скрытия поля нового имени группы
            groupSelect.onchange = function() {
                document.getElementById('newGroupNameWrap').style.display = this.value === 'new' ? 'block' : 'none';
            };
            
            // Автовыбор группы если передана
            if (preselectedGroupId) {
                groupSelect.value = String(preselectedGroupId);
            }
            
            document.getElementById('bulkProxyModal').classList.add('show');
        }
        
        function handleProxyFileSelect(input) {
            const file = input.files[0];
            if (!file) return;
            
            // Показываем название файла
            const fileNameDiv = document.getElementById('selectedFileName');
            fileNameDiv.textContent = '📄 ' + file.name;
            fileNameDiv.style.display = 'block';
            
            const reader = new FileReader();
            reader.onload = function(e) {
                const text = e.target.result;
                document.getElementById('bulkProxyText').value = text;
                updateBulkProxyCount();
            };
            reader.readAsText(file);
        }
        
        // Подсчёт строк прокси при изменении текста
        document.addEventListener('DOMContentLoaded', function() {
            const textarea = document.getElementById('bulkProxyText');
            if (textarea) {
                textarea.addEventListener('input', updateBulkProxyCount);
            }
        });
        
        function updateBulkProxyCount() {
            const text = document.getElementById('bulkProxyText').value;
            const lines = text.split('\n').filter(l => l.trim() && !l.trim().startsWith('#'));
            const count = lines.length;
            
            document.getElementById('bulkProxyCount').textContent = count;
            document.getElementById('bulkProxyPreview').style.display = count > 0 ? 'block' : 'none';
        }
        
        async function uploadBulkProxies() {
            const token = localStorage.getItem('token');
            const text = document.getElementById('bulkProxyText').value.trim();
            
            if (!text) {
                showError('bulkProxyError', 'Вставьте список прокси или загрузите файл');
                return;
            }
            
            // Определяем группу
            const groupSelect = document.getElementById('bulkProxyGroup');
            const groupValue = groupSelect.value;
            let groupId = null;
            let createGroup = null;
            
            if (!groupValue) {
                showError('bulkProxyError', 'Выберите группу для прокси');
                return;
            }
            
            if (groupValue === 'new') {
                createGroup = document.getElementById('bulkProxyNewGroup').value.trim();
                if (!createGroup) {
                    showError('bulkProxyError', 'Введите название новой группы');
                    return;
                }
            } else {
                groupId = parseInt(groupValue);
            }
            
            const btn = document.getElementById('btnUploadBulkProxies');
            btn.textContent = '⏳ Загрузка...';
            btn.disabled = true;
            
            try {
                // Определяем страну
                const countryVal = document.getElementById('bulkProxyCountry').value;
                let country = null, countryCode = null;
                if (countryVal) {
                    const parts = countryVal.split(':');
                    countryCode = parts[0];
                    country = parts[1];
                }
                
                const res = await fetch(`${API_URL}/proxies/bulk`, {
                    method: 'POST',
                    headers: { 
                        'Content-Type': 'application/json', 
                        'Authorization': `Bearer ${token}` 
                    },
                    body: JSON.stringify({
                        proxies_text: text,
                        proxy_type: document.getElementById('bulkProxyType').value,
                        name_prefix: document.getElementById('bulkProxyNamePrefix').value.trim() || null,
                        group_id: groupId,
                        create_group: createGroup,
                        country: country,
                        country_code: countryCode
                    })
                });
                
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка загрузки');
                
                // Сохраняем значение галочки ДО закрытия модалки
                const deleteFailed = document.getElementById('bulkDeleteFailed').checked;
                const uploadedGroupId = data.group_id;
                
                // Показываем результат toast + закрываем
                let msg = `Добавлено: ${data.created}`;
                if (data.duplicates > 0) msg += `, дубли: ${data.duplicates}`;
                if (data.failed > 0) msg += `, ошибок: ${data.failed}`;
                showToast(msg, data.failed > 0 ? 'error' : 'success');
                
                hideAllModals();
                loadProxies();
                
                // Авто-проверка группы после загрузки
                if (uploadedGroupId && data.created > 0) {
                    setTimeout(() => {
                        checkGroupProxiesWithDelete(uploadedGroupId, deleteFailed);
                    }, 500);
                }
                
            } catch (err) {
                showError('bulkProxyError', err.message);
            } finally {
                btn.textContent = 'Загрузить';
                btn.disabled = false;
            }
        }
        
        // ============================================================================
        // Auto-Assign Proxies Functions
        // ============================================================================
        
        async function autoAssignProxies() {
            hideAllModals();
            document.getElementById('autoAssignError').style.display = 'none';
            
            const token = localStorage.getItem('token');
            
            // Загружаем группы прокси в dropdown
            const groupSelect = document.getElementById('autoAssignGroup');
            groupSelect.innerHTML = '<option value="">Все группы прокси</option>';
            
            // Загружаем проекты аккаунтов в dropdown
            const projectSelect = document.getElementById('autoAssignProject');
            projectSelect.innerHTML = '<option value="">Все проекты</option>';
            
            try {
                // Загружаем группы прокси
                const groupsRes = await fetch(`${API_URL}/proxy-groups`, { headers: { 'Authorization': `Bearer ${token}` } });
                if (groupsRes.ok) {
                    const data = await groupsRes.json();
                    (data.groups || []).forEach(g => {
                        groupSelect.innerHTML += `<option value="${g.id}">📁 ${g.name} (${g.proxies_count})</option>`;
                    });
                }
                
                // Загружаем проекты аккаунтов
                const projRes = await fetch(`${API_URL}/projects`, { headers: { 'Authorization': `Bearer ${token}` } });
                if (projRes.ok) {
                    const data = await projRes.json();
                    (data.projects || []).forEach(p => {
                        projectSelect.innerHTML += `<option value="${p.id}">📂 ${p.name} (${p.accounts_count} акк.)</option>`;
                    });
                }
            } catch (err) {}
            
            // Добавляем обработчики изменения для пересчёта статистики
            groupSelect.onchange = updateAutoAssignStats;
            projectSelect.onchange = updateAutoAssignStats;
            document.getElementById('autoAssignOnlyActive').onchange = updateAutoAssignStats;
            
            document.getElementById('autoAssignModal').classList.add('show');
            
            // Загружаем статистику
            await updateAutoAssignStats();
        }
        
        async function updateAutoAssignStats() {
            const token = localStorage.getItem('token');
            const groupId = document.getElementById('autoAssignGroup').value;
            const projectId = document.getElementById('autoAssignProject').value;
            const onlyActive = document.getElementById('autoAssignOnlyActive').checked;
            
            try {
                // Получаем аккаунты (с фильтрацией по проекту)
                let accountsUrl = `${API_URL}/accounts`;
                if (projectId) accountsUrl += `?project_id=${projectId}`;
                
                const accRes = await fetch(accountsUrl, {
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                const accData = await accRes.json();
                const accounts = accData.accounts || [];
                const totalAccounts = accounts.length;
                
                // Получаем прокси (с фильтрацией по группе)
                let proxiesUrl = `${API_URL}/proxies`;
                if (groupId) proxiesUrl += `?group_id=${groupId}`;
                
                const proxRes = await fetch(proxiesUrl, {
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                const proxData = await proxRes.json();
                const proxies = proxData.proxies || [];
                // Считаем все прокси (или только активные)
                const availableProxies = onlyActive ? proxies.filter(p => p.is_active).length : proxies.length;
                
                document.getElementById('accountsNeedProxy').textContent = totalAccounts;
                document.getElementById('freeProxiesCount').textContent = availableProxies;
                
            } catch (err) {
                document.getElementById('accountsNeedProxy').textContent = '?';
                document.getElementById('freeProxiesCount').textContent = '?';
            }
        }
        
        async function executeAutoAssign() {
            const token = localStorage.getItem('token');
            const target = document.getElementById('autoAssignTarget').value;
            const onlyActive = document.getElementById('autoAssignOnlyActive').checked;
            const unique = document.getElementById('autoAssignUnique').checked;
            const groupId = document.getElementById('autoAssignGroup').value;
            const projectId = document.getElementById('autoAssignProject').value;
            
            try {
                // Получаем аккаунты с фильтрацией по проекту
                let accountsUrl = `${API_URL}/accounts`;
                if (projectId) {
                    accountsUrl += `?project_id=${projectId}`;
                }
                
                const accRes = await fetch(accountsUrl, {
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                const accData = await accRes.json();
                let accounts = accData.accounts || [];
                
                // Фильтруем по типу (без прокси или все)
                if (target === 'no_proxy') {
                    accounts = accounts.filter(a => !a.proxy_id);
                }
                
                if (!accounts.length) {
                    showError('autoAssignError', 'Нет аккаунтов для назначения');
                    return;
                }
                
                const payload = {
                    account_ids: accounts.map(a => a.id),
                    only_active: onlyActive,
                    unique: unique,
                    group_id: groupId ? parseInt(groupId) : null
                };
                
                const res = await fetch(`${API_URL}/proxies/auto-assign`, {
                    method: 'POST',
                    headers: { 
                        'Content-Type': 'application/json', 
                        'Authorization': `Bearer ${token}` 
                    },
                    body: JSON.stringify(payload)
                });
                
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка назначения');
                
                hideAllModals();
                showToast(data.message, 'success');
                loadAccounts();
                loadProxies();
                
            } catch (err) {
                showError('autoAssignError', err.message);
            }
        }
        
        async function saveProxy() {
            const token = localStorage.getItem('token');
            const editId = document.getElementById('proxyEditId').value;
            const saveBtn = document.getElementById('proxySaveBtn');
            
            // Берём group_id из селекта если он виден, иначе из hidden input
            const groupSelectWrap = document.getElementById('proxyGroupSelectWrap');
            let groupId = document.getElementById('proxyGroupId').value;
            
            if (groupSelectWrap && groupSelectWrap.style.display !== 'none') {
                const selectValue = document.getElementById('proxyGroupSelect').value;
                
                // Проверяем что группа выбрана (обязательно для новых прокси)
                if (!selectValue) {
                    showError('proxyError', 'Выберите группу для прокси');
                    return;
                }
                
                // Если выбрано "создать новую группу"
                if (selectValue === 'new') {
                    const newGroupName = document.getElementById('proxyNewGroupName').value.trim();
                    if (!newGroupName) {
                        showError('proxyError', 'Введите название новой группы');
                        return;
                    }
                    
                    // Сначала создаём группу
                    if (saveBtn) {
                        saveBtn.textContent = '⏳ Создание группы...';
                        saveBtn.disabled = true;
                    }
                    
                    try {
                        const groupRes = await fetch(`${API_URL}/proxy-groups`, {
                            method: 'POST',
                            headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                            body: JSON.stringify({ name: newGroupName })
                        });
                        const groupData = await groupRes.json();
                        if (!groupRes.ok) throw new Error(groupData.detail || 'Ошибка создания группы');
                        groupId = groupData.id;
                    } catch (err) {
                        showError('proxyError', 'Ошибка создания группы: ' + err.message);
                        if (saveBtn) {
                            saveBtn.textContent = 'Сохранить';
                            saveBtn.disabled = false;
                        }
                        return;
                    }
                } else {
                    groupId = selectValue;
                }
            }
            
            const payload = {
                name: document.getElementById('proxyName').value.trim() || null,
                proxy_type: document.getElementById('proxyType').value,
                host: document.getElementById('proxyHost').value.trim(),
                port: parseInt(document.getElementById('proxyPort').value),
                username: document.getElementById('proxyUsername').value.trim() || null,
                password: document.getElementById('proxyPassword').value.trim() || null,
                group_id: groupId ? parseInt(groupId) : null
            };
            
            if (!payload.host || !payload.port) {
                showError('proxyError', 'Заполните хост и порт');
                return;
            }
            
            // Показываем статус проверки для новых прокси
            if (!editId && saveBtn) {
                saveBtn.textContent = '⏳ Сохранение и проверка...';
                saveBtn.disabled = true;
            }
            
            try {
                const url = editId ? `${API_URL}/proxies/${editId}` : `${API_URL}/proxies`;
                const method = editId ? 'PUT' : 'POST';
                const res = await fetch(url, {
                    method,
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify(payload)
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка сохранения');
                hideAllModals();
                loadProxies();
            } catch (err) {
                showError('proxyError', err.message);
            } finally {
                if (saveBtn) {
                    saveBtn.textContent = 'Сохранить';
                    saveBtn.disabled = false;
                }
            }
        }
        
        async function deleteProxy(id) {
            if (!await ugConfirm('Удалить этот прокси?')) return;
            const token = localStorage.getItem('token');
            try {
                const res = await fetch(`${API_URL}/proxies/${id}`, {
                    method: 'DELETE',
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                if (!res.ok) { const d = await res.json(); throw new Error(d.detail); }
                loadProxies();
            } catch (err) {
                showToast('Ошибка: ' + err.message, 'error');
            }
        }
        
        // ============================================================================
        // Account Manager Functions
        // ============================================================================
        
        let allAccounts = [];
        let filteredAccounts = [];
        let accountsCurrentPage = 1;
        let accountsPerPage = 20;
        let selectedAccounts = new Set();
        let accountsPollingInterval = null; // Интервал для автообновления статусов
        
        function resetAllFilters() {
            document.getElementById('accountsSearch').value = '';
            document.getElementById('filterProject').value = '';
            document.getElementById('filterProxy').value = '';
            document.getElementById('filterStatus').value = '';
            document.getElementById('filterActivity').value = '';
            filterAccounts();
        }
        
        // Статусы занятости — не являются "здоровьем" аккаунта
        const ACTIVITY_STATUSES = ['warming', 'group_warming', 'parsing', 'inviting', 'monitoring'];
        
        // Определяем привязку к задачам (по данным API, а не по status)
        function getAccountTasks(a) {
            const tasks = [];
            if (a.in_inviting) tasks.push('inviting');
            if (a.in_account_warming) tasks.push('warming');
            if (a.in_group_warming) tasks.push('group_warming');
            if (a.in_parsing) tasks.push('parsing');
            if (a.in_monitoring) tasks.push('monitoring');
            return tasks;
        }
        
        function filterAccounts() {
            const searchTerm = (document.getElementById('accountsSearch').value || '').toLowerCase().trim();
            const filterProject = document.getElementById('filterProject').value;
            const filterProxy = document.getElementById('filterProxy').value;
            const filterStatus = document.getElementById('filterStatus').value;
            const filterActivity = document.getElementById('filterActivity').value;
            const searchCount = document.getElementById('accountsSearchCount');
            
            filteredAccounts = allAccounts.filter(a => {
                // Search filter
                if (searchTerm) {
                    const name = ((a.first_name || '') + ' ' + (a.last_name || '')).toLowerCase();
                    const username = (a.username || '').toLowerCase();
                    const phone = (a.phone || '').toLowerCase();
                    if (!name.includes(searchTerm) && !username.includes(searchTerm) && !phone.includes(searchTerm)) {
                        return false;
                    }
                }
                
                // Project filter
                if (filterProject) {
                    if (filterProject === 'none' && a.project_id) return false;
                    if (filterProject !== 'none' && String(a.project_id) !== filterProject) return false;
                }
                
                // Proxy filter
                if (filterProxy) {
                    if (filterProxy === 'none' && a.proxy_id) return false;
                    if (filterProxy.startsWith('group_')) {
                        const groupId = filterProxy.replace('group_', '');
                        if (String(a.proxy_group_id) !== groupId) return false;
                    } else if (filterProxy !== 'none' && String(a.proxy_id) !== filterProxy) return false;
                }
                
                // Привязки к задачам (из API)
                const tasks = getAccountTasks(a);
                const hasAnyTask = tasks.length > 0;
                
                // Определяем "здоровье" аккаунта (без учёта занятости)
                const hasSpamblock = a.status === 'spamblock' || a.spam_status === 'limited';
                const isBusy = ACTIVITY_STATUSES.includes(a.status);
                // Здоровье: если аккаунт занят — его реальный статус "active" (работает = жив)
                const healthStatus = hasSpamblock ? 'spamblock' : (isBusy ? 'active' : a.status);
                
                // Status filter (здоровье)
                if (filterStatus) {
                    if (filterStatus === 'no_session') {
                        if (a.session_data) return false;
                    } else if (filterStatus === '_problems') {
                        if (!['dead','banned','session_revoked','deactivated','frozen'].includes(a.status) && !hasSpamblock && a.session_data) return false;
                    } else if (filterStatus === 'spamblock') {
                        if (!hasSpamblock) return false;
                    } else if (filterStatus === '_resting') {
                        const isResting = a.resting_until && new Date(a.resting_until) > new Date();
                        if (!isResting) return false;
                    } else if (healthStatus !== filterStatus) {
                        return false;
                    }
                }
                
                // Activity filter (по привязке к задачам)
                if (filterActivity) {
                    if (filterActivity === '_busy') {
                        if (!hasAnyTask) return false;
                    } else if (filterActivity === '_free') {
                        if (hasAnyTask) return false;
                    } else {
                        // Конкретная задача
                        if (!tasks.includes(filterActivity)) return false;
                    }
                }
                
                return true;
            });
            
            // Update search count
            searchCount.textContent = filteredAccounts.length;
            
            accountsCurrentPage = 1;
            renderAccountsTable();
        }
        
        function populateAccountFilters() {
            // Populate projects dropdown
            const projectSelect = document.getElementById('filterProject');
            const currentProject = projectSelect.value;
            projectSelect.innerHTML = '<option value="">Все проекты</option><option value="none">Без проекта</option>';
            
            const projects = [...new Set(allAccounts.filter(a => a.project_id).map(a => JSON.stringify({id: a.project_id, name: a.project_name})))];
            projects.forEach(p => {
                const proj = JSON.parse(p);
                projectSelect.innerHTML += `<option value="${proj.id}">${proj.name}</option>`;
            });
            projectSelect.value = currentProject;
            
            // Populate proxies dropdown (сначала группы, потом отдельные)
            const proxySelect = document.getElementById('filterProxy');
            const currentProxy = proxySelect.value;
            proxySelect.innerHTML = '<option value="">Все прокси</option><option value="none">Без прокси</option>';
            
            // Группы прокси (уникальные)
            const proxyGroups = [...new Set(allAccounts.filter(a => a.proxy_group_id).map(a => JSON.stringify({id: a.proxy_group_id, name: a.proxy_group_name})))];
            if (proxyGroups.length > 0) {
                proxySelect.innerHTML += '<optgroup label="── Группы прокси ──">';
                proxyGroups.forEach(pg => {
                    const group = JSON.parse(pg);
                    proxySelect.innerHTML += `<option value="group_${group.id}">${group.name}</option>`;
                });
                proxySelect.innerHTML += '</optgroup>';
            }
            
            // Отдельные прокси
            const proxies = [...new Set(allAccounts.filter(a => a.proxy_id).map(a => JSON.stringify({id: a.proxy_id, name: a.proxy_name})))];
            if (proxies.length > 0) {
                proxySelect.innerHTML += '<optgroup label="── Отдельные прокси ──">';
                proxies.forEach(p => {
                    const prx = JSON.parse(p);
                    proxySelect.innerHTML += `<option value="${prx.id}">${prx.name}</option>`;
                });
                proxySelect.innerHTML += '</optgroup>';
            }
            proxySelect.value = currentProxy;
            
            // Обновляем счётчики в фильтре задач
            const activitySelect = document.getElementById('filterActivity');
            const currentActivity = activitySelect.value;
            const invCount = allAccounts.filter(a => a.in_inviting).length;
            const wrmCount = allAccounts.filter(a => a.in_account_warming).length;
            const grpCount = allAccounts.filter(a => a.in_group_warming).length;
            const prsCount = allAccounts.filter(a => a.in_parsing).length;
            const monCount = allAccounts.filter(a => a.in_monitoring).length;
            const busyCount = allAccounts.filter(a => a.in_inviting || a.in_account_warming || a.in_group_warming || a.in_parsing || a.in_monitoring).length;
            const freeCount = allAccounts.length - busyCount;
            activitySelect.innerHTML = `<option value="">Задачи</option>
                <option value="_free">💤 Свободные (${freeCount})</option>
                <option value="_busy">⚡ В задачах (${busyCount})</option>
                <option value="inviting">📩 Инвайтинг (${invCount})</option>
                <option value="warming">🔥 Прогрев акк. (${wrmCount})</option>
                <option value="group_warming">🔥 Прогрев группы (${grpCount})</option>
                <option value="parsing">🔍 Парсинг (${prsCount})</option>
                <option value="monitoring">🔍 Мониторинг (${monCount})</option>`;
            activitySelect.value = currentActivity;
        }
        
        async function loadAccounts() {
            return bootstrapLoadAccounts(0);
            const token = localStorage.getItem('token');
            if (!token) return;
            const tbody = document.getElementById('accountsTableBody');
            tbody.innerHTML = `<tr><td colspan="7">${getInlineLoaderHtml()}</td></tr>`;
            
            try {
                const res = await fetch(`${API_URL}/accounts`, {
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                if (!res.ok) throw new Error('Ошибка загрузки');
                const data = await res.json();
                allAccounts = data.accounts || [];
                
                // Если идёт bulk check — принудительно ставим 'checking' для ожидающих аккаунтов
                if (bulkCheckPendingIds.size > 0) {
                    allAccounts.forEach(a => {
                        if (bulkCheckPendingIds.has(a.id) && a.status !== 'checking') {
                            a.status = 'checking';
                        }
                    });
                }
                
                // Синхронизируем выбранные аккаунты - удаляем несуществующие
                const existingIds = new Set(allAccounts.map(a => a.id));
                selectedAccounts.forEach(id => {
                    if (!existingIds.has(id)) {
                        selectedAccounts.delete(id);
                    }
                });
                
                populateAccountFilters();
                filterAccounts(); // Apply current filters
                
                // Автообновление: если есть аккаунты в статусе checking — polling каждые 5 сек
                const hasChecking = allAccounts.some(a => a.status === 'checking');
                if (hasChecking && !accountsPollingInterval) {
                    console.log('[Accounts] Starting auto-refresh polling (checking accounts found)');
                    accountsPollingInterval = setInterval(loadAccountsSilent, 5000);
                } else if (!hasChecking && accountsPollingInterval) {
                    console.log('[Accounts] Stopping auto-refresh polling (no checking accounts)');
                    clearInterval(accountsPollingInterval);
                    accountsPollingInterval = null;
                }
            } catch (err) {
                tbody.innerHTML = `<tr><td colspan="7" style="padding:30px;text-align:center;color:#ff6b6b;">${escapeHtml(err.message)}</td></tr>`;
            }
        }
        
        // Тихое обновление аккаунтов без индикатора загрузки (для polling)
        async function loadAccountsSilent() {
            const token = localStorage.getItem('token');
            if (!token) return;
            
            try {
                const res = await fetch('/api/accounts', {
                    headers: { 'Authorization': 'Bearer ' + token }
                });
                if (!res.ok) return;
                const data = await res.json();
                const newAccounts = data.accounts || [];
                
                // Проверяем изменились ли статусы
                const changed = newAccounts.some(newAcc => {
                    const oldAcc = allAccounts.find(a => a.id === newAcc.id);
                    return oldAcc && (oldAcc.status !== newAcc.status || oldAcc.avatar_url !== newAcc.avatar_url);
                });
                
                if (changed) {
                    console.log('[Accounts] Status changed, updating table');
                    allAccounts = newAccounts;
                    // Защищаем pending аккаунты от перезаписи
                    if (bulkCheckPendingIds.size > 0) {
                        allAccounts.forEach(a => {
                            if (bulkCheckPendingIds.has(a.id) && a.status !== 'checking') {
                                a.status = 'checking';
                            }
                        });
                    }
                    filterAccounts();
                }
                
                // Останавливаем polling если нет checking
                const hasChecking = newAccounts.some(a => a.status === 'checking');
                if (!hasChecking && accountsPollingInterval) {
                    console.log('[Accounts] Stopping auto-refresh polling (no more checking accounts)');
                    clearInterval(accountsPollingInterval);
                    accountsPollingInterval = null;
                }
            } catch (err) {
                // Игнорируем ошибки при тихом обновлении
            }
        }
        
        function changeAccountsPerPage() {
            accountsPerPage = parseInt(document.getElementById('accountsPerPage').value);
            accountsCurrentPage = 1;
            renderAccountsTable();
        }
        
        function goToAccountsPage(page) {
            accountsCurrentPage = page;
            renderAccountsTable();
        }
        

        function renderAccountsTable() {
            const tbody = document.getElementById('accountsTableBody');
            const paginationInfo = document.getElementById('accountsPaginationInfo');
            const paginationControls = document.getElementById('accountsPagination');
            
            if (!filteredAccounts.length) {
                const searchTerm = (document.getElementById('accountsSearch').value || '').trim();
                tbody.innerHTML = searchTerm 
                    ? '<tr><td colspan="8" style="padding:30px;text-align:center;color:var(--text-secondary);">Ничего не найдено</td></tr>'
                    : '<tr><td colspan="8" style="padding:30px;text-align:center;color:var(--text-secondary);">Нет аккаунтов. Добавьте аккаунт по номеру или загрузите .session файл.</td></tr>';
                paginationInfo.textContent = '';
                paginationControls.innerHTML = '';
                return;
            }
            
            const totalPages = Math.ceil(filteredAccounts.length / accountsPerPage);
            const startIdx = (accountsCurrentPage - 1) * accountsPerPage;
            const endIdx = Math.min(startIdx + accountsPerPage, filteredAccounts.length);
            const pageAccounts = filteredAccounts.slice(startIdx, endIdx);
            
            // Update pagination info
            paginationInfo.textContent = `${startIdx + 1}-${endIdx} из ${filteredAccounts.length}`;
            
            const statusMap = {
                'active': {text:'Активный', style:'background:rgba(245,158,11,.2);color:#f59e0b;', icon:'✓'},
                'checking': {text:'Проверка...', style:'background:rgba(33,150,243,.2);color:#2196f3;', icon:'⏳'},
                'pending': {text:'Ожидает', style:'background:rgba(255,193,7,.2);color:#ffc107;', icon:'?'},
                'dead': {text:'Сессия битая', style:'background:rgba(255,107,107,.2);color:#ff6b6b;', icon:'💀'},
                'proxy_error': {text:'Ошибка прокси', style:'background:rgba(255,152,0,.2);color:#ff9800;', icon:'🌐'},
                'no_session': {text:'Без .session', style:'background:rgba(158,158,158,.2);color:#9e9e9e;', icon:'📁'},
                'banned': {text:'Забанен TG', style:'background:rgba(255,107,107,.2);color:#ff6b6b;', icon:'🚫'},
                'session_revoked': {text:'Сессия сброшена', style:'background:rgba(255,152,0,.2);color:#ff9800;', icon:'🔑'},
                'deactivated': {text:'Аккаунт удалён', style:'background:rgba(255,107,107,.2);color:#ff6b6b;', icon:'☠️'},
                'frozen': {text:'Заморожен TG', style:'background:rgba(99,179,237,.2);color:#63b3ed;', icon:'🧊'},
            };
            
            // Бейджи привязок к задачам (отдельно от статуса здоровья)
            const activityMap = {
                'warming': {text:'Прогрев', style:'background:rgba(33,150,243,.15);color:#2196f3;', icon:'🔥'},
                'group_warming': {text:'Группа', style:'background:rgba(255,152,0,.15);color:#ff9800;', icon:'🔥'},
                'parsing': {text:'Парсинг', style:'background:rgba(156,39,176,.15);color:#9c27b0;', icon:'🔍'},
                'inviting': {text:'Инвайт', style:'background:rgba(0,188,212,.15);color:#00bcd4;', icon:'📩'},
                'monitoring': {text:'Мониторинг', style:'background:rgba(76,175,80,.15);color:#4caf50;', icon:'🔍'},
            };
            
            // Функция для определения статуса здоровья (без учёта занятости)
            function getHealthStatus(acc) {
                // Спамблок — приоритет
                if (isSpamBlocked(acc)) {
                    const isPermanent = acc.spam_until === 'permanent';
                    const untilDate = !isPermanent && acc.spam_until ? acc.spam_until : null;
                    let text = 'Спамблок';
                    if (isPermanent) {
                        text = 'Спамблок Пост.';
                    } else if (untilDate) {
                        text = `Спамблок до ${untilDate}`;
                    }
                    return {
                        text: text,
                        style: isPermanent ? 'background:rgba(255,107,107,.2);color:#ff6b6b;' : 'background:rgba(255,152,0,.2);color:#ff9800;',
                        icon: '🤖',
                        isSpamblock: true
                    };
                }
                // Если занят (inviting/warming/etc) — здоровье = "Активный"
                if (ACTIVITY_STATUSES.includes(acc.status)) {
                    return statusMap['active'];
                }
                return statusMap[acc.status] || {text: acc.status, style:'background:var(--bg-hover);color:var(--text-secondary);', icon:'?'};
            }
            
            tbody.innerHTML = pageAccounts.map(a => {
                const st = getHealthStatus(a);
                const tasks = getAccountTasks(a);
                const added = new Date(a.added_at).toLocaleDateString('ru-RU');
                const canCheck = a.status !== 'no_session' && a.status !== 'checking' && !ACTIVITY_STATUSES.includes(a.status);
                
                // Бейджи привязок к задачам
                let activityBadge = '';
                tasks.forEach(t => {
                    const act = activityMap[t];
                    if (act) {
                        activityBadge += `<span style="margin-left:4px;font-size:.55rem;padding:1px 5px;border-radius:3px;${act.style}" title="${act.text}">${act.icon} ${act.text}</span>`;
                    }
                });
                
                // Отлёжка indicator
                const isResting = a.resting_until && new Date(a.resting_until) > new Date();
                let restIndicator = '';
                if (isResting) {
                    const restDate = new Date(a.resting_until);
                    const restStr = restDate.toLocaleString('ru-RU', {hour:'2-digit',minute:'2-digit',day:'2-digit',month:'2-digit'});
                    restIndicator = `<span style="margin-left:4px;font-size:.55rem;background:rgba(156,39,176,.2);color:#ab47bc;padding:1px 4px;border-radius:3px;" title="Отлёжка до ${restStr}">😴 до ${restStr}</span>`;
                }
                
                // Лимиты действий (инвайты + рассылка)
                const invLimit = Math.max(1, a.daily_invite_limit || 10);
                const invToday = a.invites_today || 0;
                const limitPct = Math.min(100, Math.round(invToday / invLimit * 100));
                const limitColor = limitPct >= 100 ? '#ff6b6b' : limitPct >= 70 ? '#ff9800' : '#f59e0b';
                
                // Компактная аватарка (загружается с авторизацией)
                const avatarUrl = a.avatar_url ? `${API_URL}${a.avatar_url}` : null;
                const avatarHtml = avatarUrl 
                    ? `<img data-auth-src="${avatarUrl}" src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" style="width:22px;height:22px;border-radius:50%;object-fit:cover;background:var(--border);" loading="lazy">`
                    : `<div style="width:22px;height:22px;border-radius:50%;background:var(--border);display:flex;align-items:center;justify-content:center;font-size:0.65rem;color:var(--text-secondary);">👤</div>`;
                
                const displayName = a.first_name ? `${a.first_name}${a.last_name ? ' ' + a.last_name : ''}` : '';
                const usernameStr = a.username ? `@${a.username}` : '';
                
                return `
                <tr onclick="onAccountRowClick(event,${a.id})" style="cursor:pointer;border-bottom:1px solid var(--border);${selectedAccounts.has(a.id) ? 'background:rgba(245,158,11,0.1);' : ''}">
                    <td style="padding:5px 6px 5px 10px;width:30px;">
                        <input type="checkbox" ${selectedAccounts.has(a.id) ? 'checked' : ''} onchange="toggleAccountSelection(${a.id}, this.checked)" style="cursor:pointer;width:16px;height:16px;accent-color:var(--primary);">
                    </td>
                    <td style="padding:5px 10px;">
                        <div style="display:flex;align-items:center;gap:8px;">
                            <span style="font-size:0.6rem;color:var(--text-secondary);min-width:20px;text-align:right;">${a.num || ''}</span>
                            ${avatarHtml}
                            <div style="min-width:0;">
                                <div style="font-weight:400;font-size:0.78rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${displayName || ''}</div>
                                ${usernameStr ? `<div style="font-size:.65rem;color:#2196f3;font-family:monospace;cursor:pointer;" onclick="copyText('${a.username}',this)" title="Скопировать">${usernameStr}</div>` : ''}
                                <div style="font-size:.65rem;color:var(--text-secondary);font-family:monospace;cursor:pointer;" onclick="copyText('${a.phone}',this)" title="Скопировать">${a.phone}</div>
                            </div>
                        </div>
                    </td>
                    <td style="padding:5px 10px;">
                        <span style="display:inline-block;padding:2px 6px;border-radius:10px;font-size:.6rem;font-weight:500;${st.style}">${st.icon} ${st.text}</span>${activityBadge}${restIndicator}
                    </td>
                    <td style="padding:5px 10px;font-size:0.75rem;">
                        ${a.proxy_name 
                            ? `<span style="cursor:pointer;text-decoration:underline;color:var(--text-secondary);" onclick="showAssignProxyModal(${a.id},'${a.phone}')">${a.proxy_name}</span>`
                            : `<span style="cursor:pointer;color:#ff6b6b;font-size:0.7rem;" onclick="showAssignProxyModal(${a.id},'${a.phone}')">Назначить</span>`
                        }
                    </td>
                    <td style="padding:5px 10px;font-size:0.72rem;white-space:nowrap;">
                        <div style="display:flex;align-items:center;gap:4px;">
                            <span style="color:${limitColor};font-weight:500;">${invToday}</span><span style="color:var(--text-secondary);font-size:.6rem;">/${invLimit}</span>
                        </div>
                        <div style="width:40px;height:3px;background:var(--border);border-radius:2px;margin-top:2px;">
                            <div style="width:${limitPct}%;height:100%;background:${limitColor};border-radius:2px;"></div>
                        </div>
                    </td>
                    <td style="padding:5px 10px;font-size:0.78rem;">${a.current_day}/15</td>
                    <td style="padding:5px 10px;color:var(--text-secondary);font-size:.7rem;">${added}</td>
                    <td style="padding:5px 10px;text-align:right;white-space:nowrap;">
                        <button onclick="showEditAccountModal(${a.id})" style="background:transparent;border:1px solid var(--border);border-radius:4px;color:var(--text-secondary);padding:3px 6px;cursor:pointer;margin-right:3px;font-size:.7rem;" title="Редактировать">✏️</button>
                        ${canCheck ? `<button onclick="checkAccount(${a.id})" style="background:transparent;border:1px solid var(--border);border-radius:4px;color:var(--text-secondary);padding:3px 6px;cursor:pointer;margin-right:3px;font-size:.7rem;" title="Проверить">🔄</button>` : ''}
                        <button onclick="deleteAccount(${a.id})" style="background:transparent;border:1px solid rgba(255,107,107,.4);border-radius:4px;color:#ff6b6b;padding:3px 6px;cursor:pointer;font-size:.7rem;" title="Удалить">🗑️</button>
                    </td>
                </tr>`;
            }).join('');
            
            // Render pagination controls
            let paginationHtml = '';
            if (totalPages > 1) {
                // Prev button
                paginationHtml += `<button onclick="goToAccountsPage(${accountsCurrentPage - 1})" ${accountsCurrentPage === 1 ? 'disabled' : ''} style="padding:4px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;${accountsCurrentPage === 1 ? 'opacity:0.5;cursor:not-allowed;' : ''}">←</button>`;
                
                // Page numbers
                const maxVisiblePages = 5;
                let startPage = Math.max(1, accountsCurrentPage - Math.floor(maxVisiblePages / 2));
                let endPage = Math.min(totalPages, startPage + maxVisiblePages - 1);
                if (endPage - startPage < maxVisiblePages - 1) {
                    startPage = Math.max(1, endPage - maxVisiblePages + 1);
                }
                
                if (startPage > 1) {
                    paginationHtml += `<button onclick="goToAccountsPage(1)" style="padding:4px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;">1</button>`;
                    if (startPage > 2) paginationHtml += `<span style="color:var(--text-secondary);">...</span>`;
                }
                
                for (let i = startPage; i <= endPage; i++) {
                    const isActive = i === accountsCurrentPage;
                    paginationHtml += `<button onclick="goToAccountsPage(${i})" style="padding:4px 10px;background:${isActive ? 'var(--primary)' : 'var(--bg-card)'};border:1px solid ${isActive ? 'var(--primary)' : 'var(--border)'};border-radius:4px;color:${isActive ? '#fff' : 'var(--text-primary)'};cursor:pointer;font-size:0.8rem;">${i}</button>`;
                }
                
                if (endPage < totalPages) {
                    if (endPage < totalPages - 1) paginationHtml += `<span style="color:var(--text-secondary);">...</span>`;
                    paginationHtml += `<button onclick="goToAccountsPage(${totalPages})" style="padding:4px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;">${totalPages}</button>`;
                }
                
                // Next button
                paginationHtml += `<button onclick="goToAccountsPage(${accountsCurrentPage + 1})" ${accountsCurrentPage === totalPages ? 'disabled' : ''} style="padding:4px 10px;background:var(--bg-card);border:1px solid var(--border);border-radius:4px;color:var(--text-primary);cursor:pointer;font-size:0.8rem;${accountsCurrentPage === totalPages ? 'opacity:0.5;cursor:not-allowed;' : ''}">→</button>`;
            }
            paginationControls.innerHTML = paginationHtml;
            
            // Update select-all checkbox state
            updateSelectAllCheckbox();
            updateBulkActionBar();
            
            // Загружаем аватарки с авторизацией
            loadAuthAvatars();
        }
        
        // ============================================================================
        // Bulk Selection & Actions
        // ============================================================================
        
        function toggleAccountSelection(accountId, checked) {
            if (checked) {
                selectedAccounts.add(accountId);
            } else {
                selectedAccounts.delete(accountId);
            }
            updateSelectAllCheckbox();
            updateBulkActionBar();
            // Update row highlight
            const rows = document.querySelectorAll('#accountsTableBody tr');
            rows.forEach(row => {
                const cb = row.querySelector('input[type=checkbox]');
                if (cb) {
                    const id = parseInt(cb.getAttribute('onchange').match(/toggleAccountSelection\((\d+)/)?.[1]);
                    if (id === accountId) {
                        row.style.background = checked ? 'rgba(245,158,11,0.1)' : '';
                    }
                }
            });
        }

        function onAccountRowClick(e, accountId) {
            // Don't toggle when clicking buttons, links, inputs, or copyable elements
            if (e.target.closest('button, a, input, [onclick*="copyText"], [onclick*="showAssignProxy"], [onclick*="showEditAccount"], [onclick*="deleteAccount"], [onclick*="checkAccount"]')) return;
            const newState = !selectedAccounts.has(accountId);
            toggleAccountSelection(accountId, newState);
            // Sync checkbox
            const row = e.currentTarget;
            const cb = row.querySelector('input[type=checkbox]');
            if (cb) cb.checked = newState;
        }
        
        function toggleSelectAllAccounts(checked) {
            // Select/deselect ALL FILTERED accounts (not just current page)
            if (checked) {
                filteredAccounts.forEach(a => selectedAccounts.add(a.id));
            } else {
                filteredAccounts.forEach(a => selectedAccounts.delete(a.id));
            }
            renderAccountsTable();
        }
        
        function selectAllFiltered() {
            filteredAccounts.forEach(a => selectedAccounts.add(a.id));
            renderAccountsTable();
        }
        
        function updateSelectAllCheckbox() {
            const cb = document.getElementById('selectAllAccounts');
            if (!cb) return;
            // Check against ALL filtered, not just current page
            if (!filteredAccounts.length) {
                cb.checked = false;
                cb.indeterminate = false;
                return;
            }
            const selectedCount = filteredAccounts.filter(a => selectedAccounts.has(a.id)).length;
            cb.checked = selectedCount === filteredAccounts.length;
            cb.indeterminate = selectedCount > 0 && selectedCount < filteredAccounts.length;
        }
        
        function updateBulkActionBar() {
            const bar = document.getElementById('bulkActionBar');
            const countEl = document.getElementById('bulkSelectedCount');
            if (!bar) return;
            if (selectedAccounts.size > 0) {
                bar.style.display = 'block';
                const n = selectedAccounts.size;
                const word = n === 1 ? 'аккаунт' : (n < 5 ? 'аккаунта' : 'аккаунтов');
                let text = `Выбрано ${n} ${word}`;
                // If not all filtered are selected, offer to select all
                if (n < filteredAccounts.length) {
                    text += `  <span onclick="selectAllFiltered()" style="cursor:pointer;text-decoration:underline;color:rgba(255,255,255,0.7);font-weight:400;font-size:0.78rem;">Выбрать все ${filteredAccounts.length}</span>`;
                }
                countEl.innerHTML = text;
            } else {
                bar.style.display = 'none';
            }
        }
        
        function clearAccountSelection() {
            selectedAccounts.clear();
            renderAccountsTable();
        }
        
        function showBulkProjectModal() {
            if (!selectedAccounts.size) return;
            let projectOptions = `<option value="">Без проекта</option>`;
            allProjects.forEach(p => {
                projectOptions += `<option value="${p.id}">${p.name}</option>`;
            });
            
            const modal = document.createElement('div');
            modal.className = 'modal-overlay show';
            modal.id = 'bulkProjectModal';
            modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
            modal.innerHTML = `
                <div class="modal" style="max-width:420px;">
                    <h2 style="margin-bottom:4px;font-size:1rem;">📁 Изменить проект для ${selectedAccounts.size} акк.</h2>
                    <p style="color:var(--text-secondary);font-size:0.75rem;margin-bottom:12px;">Выберите проект</p>
                    <div class="input-group">
                        <label>Проект</label>
                        <select id="bulkProjectSelect" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                            ${projectOptions}
                        </select>
                    </div>
                    <div id="bulkProjectError" class="error"></div>
                    <div class="modal-buttons">
                        <button class="btn btn-secondary" onclick="document.getElementById('bulkProjectModal').remove()">Отмена</button>
                        <button id="bulkProjectBtn" class="btn" onclick="executeBulkProject()">Применить</button>
                    </div>
                </div>`;
            document.body.appendChild(modal);
        }
        
        async function executeBulkProject() {
            const token = localStorage.getItem('token');
            const projectId = document.getElementById('bulkProjectSelect').value;
            const btn = document.getElementById('bulkProjectBtn');
            const errorEl = document.getElementById('bulkProjectError');
            
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner"></span>Применяю...';
            errorEl.style.display = 'none';
            
            try {
                const res = await fetch(`${API_URL}/accounts/bulk/project`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify({ account_ids: [...selectedAccounts], project_id: projectId ? parseInt(projectId) : null })
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка');
                document.getElementById('bulkProjectModal').remove();
                selectedAccounts.clear();
                loadAccounts();
            } catch (err) {
                errorEl.textContent = err.message;
                errorEl.style.display = 'block';
                btn.disabled = false;
                btn.textContent = 'Применить';
            }
        }
        
        function showBulkChangeProxyModal() {
            if (!selectedAccounts.size) return;
            
            // Опции групп прокси
            let groupOptions = '';
            cachedProxyGroups.forEach(g => {
                groupOptions += `<option value="${g.id}">📁 ${g.name} (${g.active_count || g.proxies_count || '?'} прокси)</option>`;
            });
            
            // Опции отдельных прокси — с группами
            let proxyOptions = generateProxyOptionsHtml(cachedProxies, cachedProxyGroups, false);
            
            const modal = document.createElement('div');
            modal.className = 'modal-overlay show';
            modal.id = 'bulkChangeProxyModal';
            modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
            modal.innerHTML = `
                <div class="modal" style="max-width:480px;">
                    <h2 style="margin-bottom:4px;font-size:1rem;">🌐 Изменить прокси для ${selectedAccounts.size} акк.</h2>
                    <p style="color:var(--text-secondary);font-size:0.75rem;margin-bottom:12px;">Назначить из группы или выбрать один прокси</p>
                    
                    <div style="display:flex;gap:4px;margin-bottom:12px;">
                        <button id="bulkProxyTabGroup" class="btn" style="flex:1;padding:8px;font-size:0.8rem;background:var(--accent);" onclick="switchBulkProxyTab('group')">📁 Из группы</button>
                        <button id="bulkProxyTabSingle" class="btn btn-secondary" style="flex:1;padding:8px;font-size:0.8rem;" onclick="switchBulkProxyTab('single')">🔌 Один прокси</button>
                    </div>
                    
                    <div id="bulkProxyGroupTab">
                        <div class="input-group">
                            <label>Группа прокси</label>
                            <select id="bulkProxyGroupSelect" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                                ${groupOptions || '<option value="" disabled>Нет групп прокси</option>'}
                            </select>
                        </div>
                        <p style="color:var(--text-secondary);font-size:0.7rem;margin-top:4px;">Каждому аккаунту будет назначен уникальный прокси из группы (round-robin)</p>
                    </div>
                    
                    <div id="bulkProxySingleTab" style="display:none;">
                        <div class="input-group">
                            <label>Прокси</label>
                            <select id="bulkProxySelect" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                                ${proxyOptions}
                            </select>
                        </div>
                    </div>
                    
                    <div id="bulkProxyError" class="error"></div>
                    <div class="modal-buttons">
                        <button class="btn btn-secondary" onclick="document.getElementById('bulkChangeProxyModal').remove()">Отмена</button>
                        <button id="bulkProxyBtn" class="btn" onclick="executeBulkProxy()">Применить</button>
                    </div>
                </div>`;
            document.body.appendChild(modal);
        }
        
        let bulkProxyMode = 'group'; // 'group' или 'single'
        function switchBulkProxyTab(tab) {
            bulkProxyMode = tab;
            document.getElementById('bulkProxyGroupTab').style.display = tab === 'group' ? 'block' : 'none';
            document.getElementById('bulkProxySingleTab').style.display = tab === 'single' ? 'block' : 'none';
            const btnGroup = document.getElementById('bulkProxyTabGroup');
            const btnSingle = document.getElementById('bulkProxyTabSingle');
            if (tab === 'group') {
                btnGroup.style.background = 'var(--accent)'; btnGroup.className = 'btn';
                btnSingle.style.background = ''; btnSingle.className = 'btn btn-secondary';
            } else {
                btnSingle.style.background = 'var(--accent)'; btnSingle.className = 'btn';
                btnGroup.style.background = ''; btnGroup.className = 'btn btn-secondary';
            }
        }
        
        async function executeBulkProxy() {
            const token = localStorage.getItem('token');
            const btn = document.getElementById('bulkProxyBtn');
            const errorEl = document.getElementById('bulkProxyError');
            
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner"></span>Применяю...';
            errorEl.style.display = 'none';
            
            try {
                let url, body;
                if (bulkProxyMode === 'group') {
                    const groupId = document.getElementById('bulkProxyGroupSelect').value;
                    if (!groupId) throw new Error('Выберите группу прокси');
                    url = `${API_URL}/accounts/bulk/proxy-group`;
                    body = { account_ids: [...selectedAccounts], group_id: parseInt(groupId) };
                } else {
                    const proxyId = document.getElementById('bulkProxySelect').value;
                    url = `${API_URL}/accounts/bulk/proxy`;
                    body = { account_ids: [...selectedAccounts], proxy_id: proxyId ? parseInt(proxyId) : null };
                }
                
                const res = await fetch(url, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify(body)
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка');
                document.getElementById('bulkChangeProxyModal').remove();
                selectedAccounts.clear();
                showToast(data.message || 'Прокси обновлены', 'success');
                loadAccounts();
            } catch (err) {
                errorEl.textContent = err.message;
                errorEl.style.display = 'block';
                btn.disabled = false;
                btn.textContent = 'Применить';
            }
        }
        
        function showBulkDayModal() {
            if (!selectedAccounts.size) return;
            const modal = document.createElement('div');
            modal.className = 'modal-overlay show';
            modal.id = 'bulkDayModal';
            modal.onclick = (e) => { if (e.target === modal) modal.remove(); };
            modal.innerHTML = `
                <div class="modal" style="max-width:420px;">
                    <h2 style="margin-bottom:4px;font-size:1rem;">📅 Изменить день для ${selectedAccounts.size} акк.</h2>
                    <p style="color:var(--text-secondary);font-size:0.75rem;margin-bottom:12px;">Укажите день прогрева (0–15)</p>
                    <div class="input-group">
                        <label>День прогрева</label>
                        <input type="number" id="bulkDayInput" min="0" max="15" value="1" style="width:100%;padding:8px 10px;background:var(--bg-dark);border:1px solid var(--border);border-radius:5px;color:var(--text-primary);font-size:0.85rem;">
                    </div>
                    <div id="bulkDayError" class="error"></div>
                    <div class="modal-buttons">
                        <button class="btn btn-secondary" onclick="document.getElementById('bulkDayModal').remove()">Отмена</button>
                        <button id="bulkDayBtn" class="btn" onclick="executeBulkDay()">Применить</button>
                    </div>
                </div>`;
            document.body.appendChild(modal);
            document.getElementById('bulkDayInput').focus();
        }
        
        async function executeBulkDay() {
            const token = localStorage.getItem('token');
            const day = parseInt(document.getElementById('bulkDayInput').value);
            const btn = document.getElementById('bulkDayBtn');
            const errorEl = document.getElementById('bulkDayError');
            
            if (isNaN(day) || day < 0 || day > 15) {
                errorEl.textContent = 'День должен быть от 0 до 15';
                errorEl.style.display = 'block';
                return;
            }
            
            btn.disabled = true;
            btn.innerHTML = '<span class="spinner"></span>Применяю...';
            errorEl.style.display = 'none';
            
            try {
                const res = await fetch(`${API_URL}/accounts/bulk/day`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify({ account_ids: [...selectedAccounts], day: day })
                });
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка');
                document.getElementById('bulkDayModal').remove();
                selectedAccounts.clear();
                loadAccounts();
            } catch (err) {
                errorEl.textContent = err.message;
                errorEl.style.display = 'block';
                btn.disabled = false;
                btn.textContent = 'Применить';
            }
        }
        
        // Универсальная функция для опасных операций с подтверждением
        let pendingSensitiveOperation = null;
        
        async function executeSensitiveOperation(operationFn, operationName) {
            // Сначала пробуем без кода (может есть активная сессия)
            const result = await operationFn(null);
            if (result === 'need_code') {
                // Нет сессии — запрашиваем код
                pendingSensitiveOperation = operationFn;
                showSensitiveCodeModal(operationName);
            }
            return result;
        }
        
        async function showSensitiveCodeModal(operationName) {
            const token = localStorage.getItem('token');
            
            try {
                const formData = new FormData();
                formData.append('action', 'sensitive_operation');
                
                const res = await fetch(`${API_URL}/auth/request-action-code`, {
                    method: 'POST',
                    headers: { 'Authorization': `Bearer ${token}` },
                    body: formData
                });
                
                const data = await res.json();
                
                if (!res.ok) {
                    showToast(data.detail || 'Ошибка отправки кода', 'error');
                    return;
                }
                
                document.getElementById('sensitiveCodeEmail').textContent = data.message;
                document.getElementById('sensitiveCodeOperation').textContent = operationName;
                document.getElementById('sensitiveCodeInput').value = '';
                document.getElementById('sensitiveCodeError').style.display = 'none';
                openModal('sensitiveCodeModal');
                
                setTimeout(() => document.getElementById('sensitiveCodeInput').focus(), 100);
                
            } catch (err) {
                showToast('Ошибка: ' + err.message, 'error');
            }
        }
        
        async function submitSensitiveCode() {
            const code = document.getElementById('sensitiveCodeInput').value.trim();
            if (code.length !== 6) {
                document.getElementById('sensitiveCodeError').textContent = 'Введите 6-значный код';
                document.getElementById('sensitiveCodeError').style.display = 'block';
                return;
            }
            
            closeModal('sensitiveCodeModal');
            
            if (pendingSensitiveOperation) {
                await pendingSensitiveOperation(code);
                pendingSensitiveOperation = null;
            }
        }
        
        async function bulkDeleteAccounts() {
            if (!selectedAccounts.size) return;
            const n = selectedAccounts.size;
            const word = n === 1 ? 'аккаунт' : (n < 5 ? 'аккаунта' : 'аккаунтов');
            if (!await ugConfirm(`Удалить ${n} ${word}? Это действие необратимо!`)) return;
            
            await executeSensitiveOperation(
                (code) => doBulkDelete(code),
                'Удаление аккаунтов'
            );
        }
        
        async function doBulkDelete(confirmationCode) {
            const token = localStorage.getItem('token');
            try {
                const body = { account_ids: [...selectedAccounts] };
                if (confirmationCode) body.confirmation_code = confirmationCode;
                
                const res = await fetch(`${API_URL}/accounts/bulk/delete`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify(body)
                });
                
                if (res.status === 429) {
                    const data = await res.json();
                    showToast(data.detail || 'Слишком много попыток. Подождите 5 минут.', 'error');
                    return 'error';
                }
                
                if (res.status === 403) {
                    const data = await res.json();
                    if (data.detail && data.detail.includes('Требуется подтверждение')) {
                        return 'need_code';
                    }
                    throw new Error(data.detail || 'Доступ запрещён');
                }
                
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка');
                
                selectedAccounts.clear();
                loadAccounts();
                showToast(`Удалено ${data.deleted} аккаунтов`, 'success');
                return 'success';
            } catch (err) {
                showToast('Ошибка удаления: ' + err.message, 'error');
                return 'error';
            }
        }
        
        let bulkCheckAborted = false;
        let bulkCheckRunning = false;
        let bulkCheckController = null;
        // Множество ID аккаунтов ожидающих проверки — 'checking' не затирается loadAccounts
        let bulkCheckPendingIds = new Set();
        
        function stopBulkCheck() {
            bulkCheckAborted = true;
            bulkCheckRunning = false;
            if (bulkCheckController) bulkCheckController.abort();
            bulkCheckPendingIds.clear();
            updateBulkCheckProgress(0, 0, true);
            localStorage.removeItem('bulkCheck');
            loadAccounts();
        }
        
        function updateBulkCheckProgress(checked, total, done = false) {
            const progressEl = document.getElementById('bulkCheckProgress');
            const textEl = document.getElementById('bulkCheckProgressText');
            if (!progressEl || !textEl) return;
            
            if (done) {
                progressEl.style.display = 'none';
                bulkCheckRunning = false;
            } else {
                progressEl.style.display = 'inline-flex';
                textEl.textContent = `Проверка ${checked}/${total}`;
            }
        }
        
        // Восстанавливаем состояние bulk check после обновления страницы (авто, без confirm)
        function resumeBulkCheckIfNeeded() {
            const saved = localStorage.getItem('bulkCheck');
            if (!saved) return;
            try {
                const state = JSON.parse(saved);
                // Если прошло больше 15 минут — считаем что проверка завершилась/упала
                if (Date.now() - state.startedAt > 15 * 60 * 1000) {
                    localStorage.removeItem('bulkCheck');
                    return;
                }
                const remaining = state.remaining || [];
                if (remaining.length === 0) {
                    localStorage.removeItem('bulkCheck');
                    return;
                }
                
                // Автоматически продолжаем проверку без confirm
                console.log(`[BulkCheck] Восстановление: осталось ${remaining.length} из ${state.total}`);
                bulkCheckRunning = false;
                bulkCheckAborted = false;
                
                // Помечаем оставшиеся как checking в UI
                bulkCheckPendingIds = new Set(remaining);
                remaining.forEach(id => {
                    const acc = allAccounts.find(a => a.id === id);
                    if (acc && acc.status !== 'checking') acc.status = 'checking';
                });
                renderAccountsTable();
                
                // Запускаем проверку оставшихся
                _runBulkCheck(remaining, state.checkSpam, state.total - remaining.length, state.total, state.errors || 0, state.spamLimited || 0);
            } catch (e) {
                localStorage.removeItem('bulkCheck');
            }
        }
        
        async function bulkCheckAccounts() {
            if (!selectedAccounts.size) return;
            if (bulkCheckRunning) {
                showToast('Проверка уже запущена. Нажмите на индикатор вверху чтобы остановить.', 'info');
                return;
            }
            
            const checkSpam = document.getElementById('checkSpamBot')?.checked || false;
            const accountIds = [...selectedAccounts];
            const total = accountIds.length;
            
            // Сразу помечаем все как "checking" в UI
            bulkCheckPendingIds = new Set(accountIds);
            accountIds.forEach(id => {
                const acc = allAccounts.find(a => a.id === id);
                if (acc) acc.status = 'checking';
            });
            
            // Снимаем выделение и перерисовываем
            selectedAccounts.clear();
            renderAccountsTable();
            
            _runBulkCheck(accountIds, checkSpam, 0, total, 0, 0);
        }
        
        async function _runBulkCheck(accountIds, checkSpam, startChecked, total, startErrors, startSpamLimited) {
            const token = localStorage.getItem('token');
            
            bulkCheckAborted = false;
            bulkCheckRunning = true;
            bulkCheckController = new AbortController();
            updateBulkCheckProgress(startChecked, total);
            
            // Сохраняем начальное состояние
            localStorage.setItem('bulkCheck', JSON.stringify({
                remaining: [...accountIds], checkSpam, total,
                errors: startErrors, spamLimited: startSpamLimited, startedAt: Date.now()
            }));
            
            let checked = startChecked;
            let errors = startErrors;
            let spamLimited = startSpamLimited;
            let remaining = [...accountIds];
            
            // SpamBot: 3 параллельно (разные прокси), без спама — 5
            const PARALLEL = checkSpam ? 3 : 5;
            const DELAY_BASE = checkSpam ? 1200 : 300;
            const DELAY_JITTER = checkSpam ? 800 : 200;
            
            for (let i = 0; i < accountIds.length; i += PARALLEL) {
                if (bulkCheckAborted) break;
                
                const batch = accountIds.slice(i, i + PARALLEL);
                
                await Promise.all(batch.map(async (id) => {
                    if (bulkCheckAborted) return;
                    try {
                        const url = checkSpam 
                            ? `${API_URL}/accounts/${id}/check?check_spam=true` 
                            : `${API_URL}/accounts/${id}/check`;
                        const reqCtrl = new AbortController();
                        const reqTimer = setTimeout(() => reqCtrl.abort(), 90000);
                        let res;
                        try {
                            res = await fetch(url, {
                                method: 'POST',
                                headers: { 'Authorization': `Bearer ${token}` },
                                signal: reqCtrl.signal
                            });
                        } finally {
                            clearTimeout(reqTimer);
                        }
                        if (!res.ok) {
                            errors++;
                        } else {
                            const data = await res.json();
                            const acc = allAccounts.find(a => a.id === id);
                            if (acc) {
                                const oldStatus = acc.status;
                                if (data.skipped) { /* не меняем */ }
                                else if (data.is_valid === true) {
                                    if (data.spam_status === 'limited') acc.status = 'spamblock';
                                    else if (data.spam_status === 'blocked') acc.status = 'frozen';
                                    else acc.status = 'active';
                                }
                                else if (data.is_valid === false) {
                                    if (data.error?.toLowerCase().includes('frozen')) acc.status = 'frozen';
                                    else if (data.error?.includes('session_revoked') || data.error?.toLowerCase().includes('auth')) acc.status = 'session_revoked';
                                    else if (/таймаут|timeout|прокси|proxy|socks|network|connection refused|connection reset|connect/i.test(data.error || '')) acc.status = 'proxy_error';
                                    else acc.status = oldStatus || 'active';
                                }
                                else acc.status = 'active';
                                if (data.spam_status === 'ok') {
                                    acc.spam_status = 'ok'; acc.spam_until = null;
                                } else if (data.spam_status) {
                                    acc.spam_status = data.spam_status; acc.spam_until = data.spam_until || null;
                                } else if (data.is_valid === false) {
                                    acc.spam_status = null; acc.spam_until = null;
                                }
                            }
                            if (checkSpam && (data.spam_status === 'limited' || data.spam_status === 'blocked')) spamLimited++;
                        }
                    } catch (err) {
                        if (err.name === 'AbortError') return;
                        errors++;
                    }
                    // Убираем из pending — loadAccounts больше не затрёт статус
                    bulkCheckPendingIds.delete(id);
                    remaining = remaining.filter(r => r !== id);
                    checked++;
                    // Обновляем UI сразу после каждого аккаунта
                    renderAccountsTable();
                    if (!bulkCheckAborted) {
                        updateBulkCheckProgress(checked, total);
                        // Сохраняем оставшиеся ID (не checked counter — а конкретный список)
                        localStorage.setItem('bulkCheck', JSON.stringify({
                            remaining, checkSpam, total, errors, spamLimited, startedAt: Date.now()
                        }));
                    }
                }));
                
                // Перерисовываем таблицу
                renderAccountsTable();
                
                if (i + PARALLEL < accountIds.length && !bulkCheckAborted) {
                    const jitter = DELAY_JITTER ? Math.floor(Math.random() * DELAY_JITTER) : 0;
                    await new Promise(r => setTimeout(r, DELAY_BASE + jitter));
                }
            }
            
            // Готово
            updateBulkCheckProgress(0, 0, true);
            localStorage.removeItem('bulkCheck');
            bulkCheckPendingIds.clear();
            bulkCheckRunning = false;
            
            loadAccounts();
            
            let msg = bulkCheckAborted 
                ? `Проверка остановлена. Проверено: ${checked}/${total}`
                : `✅ Проверено: ${checked} аккаунтов` + (errors ? `, ошибок: ${errors}` : '');
            if (checkSpam && spamLimited > 0) {
                msg += `\n⚠️ SpamBot ограничения: ${spamLimited} аккаунтов`;
            }
            showToast(msg, bulkCheckAborted ? 'info' : 'success');
        }
        
        async function checkAccount(id, checkSpam = false) {
            const token = localStorage.getItem('token');
            // Сразу показываем статус "checking" в таблице
            const acc = allAccounts.find(a => a.id === id);
            if (acc) { acc.status = 'checking'; renderAccountsTable(); }
            // Отмечаем как pending чтобы loadAccounts не затёр
            bulkCheckPendingIds.add(id);
            try {
                const url = checkSpam 
                    ? `${API_URL}/accounts/${id}/check?check_spam=true` 
                    : `${API_URL}/accounts/${id}/check`;
                const res = await fetch(url, {
                    method: 'POST',
                    headers: { 'Authorization': `Bearer ${token}` }
                });
                const data = await res.json();
                // Обновляем локальный статус сразу
                if (acc) {
                    if (data.skipped) { /* не меняем */ }
                    else if (data.is_valid === true) {
                        if (data.spam_status === 'limited') acc.status = 'spamblock';
                        else if (data.spam_status === 'blocked') acc.status = 'frozen';
                        else acc.status = 'active';
                    }
                    else if (data.is_valid === false) acc.status = 'dead';
                    if (data.spam_status === 'ok') { acc.spam_status = 'ok'; acc.spam_until = null; }
                    else if (data.spam_status) { acc.spam_status = data.spam_status; acc.spam_until = data.spam_until || null; }
                    else if (data.is_valid === false) { acc.spam_status = null; acc.spam_until = null; }
                    renderAccountsTable();
                }
            } catch (err) {
                // ignore
            }
            bulkCheckPendingIds.delete(id);
            loadAccounts();
        }
        
        async function populateProxySelects() {
            const token = localStorage.getItem('token');
            try {
                const [proxiesRes, groupsRes] = await Promise.all([
                    fetch(`${API_URL}/proxies`, { headers: { 'Authorization': `Bearer ${token}` }}),
                    fetch(`${API_URL}/proxy-groups`, { headers: { 'Authorization': `Bearer ${token}` }})
                ]);
                
                let proxies = [];
                let groups = [];
                
                if (proxiesRes.ok) {
                    const data = await proxiesRes.json();
                    proxies = data.proxies || [];
                }
                if (groupsRes.ok) {
                    const data = await groupsRes.json();
                    groups = data.groups || [];
                }
                
                // Селект для загрузки сессии - с группами для авто-назначения
                const uploadSel = document.getElementById('uploadProxySelect');
                if (uploadSel) {
                    const currentVal = uploadSel.value;
                    uploadSel.innerHTML = generateProxyOptionsHtml(proxies, groups, true);
                    if (currentVal) uploadSel.value = currentVal;
                }
                
                // Селект для добавления аккаунта по номеру — с группами авто-назначения
                const accountSel = document.getElementById('accountProxySelect');
                if (accountSel) {
                    const currentVal = accountSel.value;
                    accountSel.innerHTML = generateProxyOptionsHtml(proxies, groups, true);
                    if (currentVal) accountSel.value = currentVal;
                }
                
                // Селект для назначения прокси — с группами
                const plainSelects = ['assignProxySelect'];
                plainSelects.forEach(selectId => {
                    const sel = document.getElementById(selectId);
                    if (!sel) return;
                    const currentVal = sel.value;
                    sel.innerHTML = generateProxyOptionsHtml(proxies, groups, true);
                    if (currentVal) sel.value = currentVal;
                });
            } catch (e) {}
        }
        
        function showAddAccountModal() {
            hideAllModals();
            document.getElementById('accountPhone').value = '';
            document.getElementById('phoneAuthCode') && (document.getElementById('phoneAuthCode').value = '');
            document.getElementById('phoneAuth2FA') && (document.getElementById('phoneAuth2FA').value = '');
            document.getElementById('addAccountError').style.display = 'none';
            document.getElementById('phoneAuthLoading').style.display = 'none';
            // Reset to step 1
            document.getElementById('phoneAuthStep1').style.display = 'block';
            document.getElementById('phoneAuthStep2').style.display = 'none';
            document.getElementById('phoneAuthStep3').style.display = 'none';
            populateProxySelects();
            populateAccountProjectSelect();
            document.getElementById('addAccountModal').classList.add('show');
        }
        
        async function populateAccountProjectSelect() {
            const token = localStorage.getItem('token');
            const sel = document.getElementById('accountProjectSelect');
            if (!sel) return;
            try {
                const res = await fetch(`${API_URL}/projects`, { headers: { 'Authorization': `Bearer ${token}` }});
                if (!res.ok) return;
                const data = await res.json();
                const projects = data.projects || [];
                sel.innerHTML = '<option value="">Без проекта</option>' + projects.map(p => 
                    `<option value="${p.id}">${p.name}</option>`
                ).join('');
            } catch (e) {}
        }
        
        // Phone saved between steps
        let _phoneAuthPhone = '';

        function _phoneAuthSetLoading(show, text) {
            document.getElementById('phoneAuthLoading').style.display = show ? 'block' : 'none';
            if (text) document.getElementById('phoneAuthLoadingText').textContent = text;
            document.querySelectorAll('#addAccountModal .btn').forEach(b => b.disabled = show);
        }

        async function phoneAuthSendCode() {
            const token = localStorage.getItem('token');
            const phone = document.getElementById('accountPhone').value.trim();
            const proxyValue = document.getElementById('accountProxySelect').value;
            const projectId = document.getElementById('accountProjectSelect').value;
            document.getElementById('addAccountError').style.display = 'none';

            if (!phone) { showError('addAccountError', 'Введите номер телефона'); return; }

            _phoneAuthPhone = phone;
            const isGroup = proxyValue && proxyValue.startsWith('group:');
            _phoneAuthSetLoading(true, isGroup ? 'Подключение через прокси...' : 'Подключение...');

            const body = { phone, project_id: projectId ? parseInt(projectId) : null };
            if (isGroup) {
                body.proxy_group_id = parseInt(proxyValue.replace('group:', ''));
            } else if (proxyValue) {
                body.proxy_id = parseInt(proxyValue);
            }

            try {
                const res = await fetch(`${API_URL}/accounts/auth/send-code`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify(body)
                });
                const data = await parseApiResponse(res);
                if (!res.ok) throw new Error(getErrorMessage(data, 'Ошибка отправки кода'));

                // Показываем инфо о retry прокси если были
                let hint = data.message || 'Код отправлен в Telegram';
                if (data.retried_proxies && data.retried_proxies.length > 0) {
                    hint += ` (пропущено ${data.retried_proxies.length} нерабочих прокси)`;
                }
                if (data.used_proxy) {
                    hint += ` • Прокси: ${data.used_proxy}`;
                }

                // Switch to step 2
                document.getElementById('phoneAuthStep1').style.display = 'none';
                document.getElementById('phoneAuthStep2').style.display = 'block';
                document.getElementById('phoneAuthCodeHint').textContent = hint;
                document.getElementById('phoneAuthCode').value = '';
                document.getElementById('phoneAuthCode').focus();
            } catch (err) {
                showError('addAccountError', err.message);
            } finally {
                _phoneAuthSetLoading(false);
            }
        }

        async function phoneAuthVerifyCode() {
            const token = localStorage.getItem('token');
            const code = document.getElementById('phoneAuthCode').value.trim();
            document.getElementById('addAccountError').style.display = 'none';

            if (!code) { showError('addAccountError', 'Введите код'); return; }

            _phoneAuthSetLoading(true, 'Проверка кода...');

            try {
                const res = await fetch(`${API_URL}/accounts/auth/verify-code`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify({ phone: _phoneAuthPhone, code })
                });
                const data = await parseApiResponse(res);
                if (!res.ok) throw new Error(getErrorMessage(data, 'Неверный код'));

                if (data.needs_2fa) {
                    // Switch to step 3
                    document.getElementById('phoneAuthStep2').style.display = 'none';
                    document.getElementById('phoneAuthStep3').style.display = 'block';
                    document.getElementById('phoneAuth2FA').value = '';
                    document.getElementById('phoneAuth2FA').focus();
                } else {
                    // Success — no 2FA needed
                    hideAllModals();
                    loadAccounts();
                    // Auto-check to download avatar
                    if (data.account && data.account.id) {
                        setTimeout(() => checkAccount(data.account.id), 500);
                    }
                }
            } catch (err) {
                showError('addAccountError', err.message);
            } finally {
                _phoneAuthSetLoading(false);
            }
        }

        async function phoneAuthVerify2FA() {
            const token = localStorage.getItem('token');
            const password = document.getElementById('phoneAuth2FA').value;
            document.getElementById('addAccountError').style.display = 'none';

            if (!password) { showError('addAccountError', 'Введите пароль 2FA'); return; }

            _phoneAuthSetLoading(true, 'Вход с 2FA...');

            try {
                const res = await fetch(`${API_URL}/accounts/auth/verify-2fa`, {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
                    body: JSON.stringify({ phone: _phoneAuthPhone, password })
                });
                const data = await parseApiResponse(res);
                if (!res.ok) throw new Error(getErrorMessage(data, 'Неверный пароль 2FA'));

                hideAllModals();
                loadAccounts();
                // Auto-check to download avatar
                if (data.account && data.account.id) {
                    setTimeout(() => checkAccount(data.account.id), 500);
                }
            } catch (err) {
                showError('addAccountError', err.message);
            } finally {
                _phoneAuthSetLoading(false);
            }
        }

        function phoneAuthBack() {
            document.getElementById('addAccountError').style.display = 'none';
            const step2 = document.getElementById('phoneAuthStep2');
            const step3 = document.getElementById('phoneAuthStep3');
            if (step3.style.display !== 'none') {
                step3.style.display = 'none';
                step2.style.display = 'block';
            } else {
                step2.style.display = 'none';
                document.getElementById('phoneAuthStep1').style.display = 'block';
            }
        }

        function showUploadSessionModal() {
            hideAllModals();
            document.getElementById('uploadPhone').value = '';
            document.getElementById('uploadSessionFile').value = '';
            document.getElementById('uploadError').style.display = 'none';
            populateProxySelects();
            populateUploadProjectSelect();
            document.getElementById('uploadSessionModal').classList.add('show');
        }
        
        async function populateUploadProjectSelect() {
            const token = localStorage.getItem('token');
            const sel = document.getElementById('uploadProjectSelect');
            if (!sel) return;
            try {
                const res = await fetch(`${API_URL}/projects`, { headers: { 'Authorization': `Bearer ${token}` }});
                if (!res.ok) return;
                const data = await res.json();
                const projects = data.projects || [];
                sel.innerHTML = '<option value="">Без проекта</option>' + projects.map(p => 
                    `<option value="${p.id}">${p.name}</option>`
                ).join('');
            } catch (e) {}
        }

        // ============================================================================
        // Tdata Upload Functions
        // ============================================================================
        
        let tdataFile = null;
        
        // Хелпер: красивый label для прокси
        function getProxyLabel(p) {
            const name = p.name || `${p.host}:${p.port}`;
            const group = cachedProxyGroups?.find(g => g.id === p.group_id);
            const groupName = group ? `[${group.name}] ` : '';
            const country = p.country || p.country_code || '';
            return `${groupName}${name}${country ? ' (' + country + ')' : ''}`;
        }
        
        // Генерация HTML опций для прокси селекта (с группами для авто-назначения)
        function generateProxyOptionsHtml(proxies, groups, includeAutoGroups = true) {
            let html = '<option value="">Без прокси</option>';
            
            // Добавляем группы для авто-назначения
            if (includeAutoGroups && groups && groups.length > 0) {
                html += '<optgroup label="🔄 Авто из группы">';
                groups.forEach(g => {
                    const groupProxies = proxies.filter(p => p.group_id === g.id);
                    const freeCount = groupProxies.filter(p => p.is_active && p.accounts_count === 0).length;
                    const country = g.country || groupProxies.find(p => p.country)?.country || '';
                    html += `<option value="group:${g.id}">🔄 ${g.name}${country ? ' (' + country + ')' : ''} — ${freeCount} своб.</option>`;
                });
                html += '</optgroup>';
            }
            
            // Добавляем отдельные прокси, группированные по группам
            if (proxies && proxies.length > 0) {
                // Сначала прокси с группами
                const groupedByGroup = {};
                const ungrouped = [];
                proxies.forEach(p => {
                    if (p.group_id) {
                        if (!groupedByGroup[p.group_id]) groupedByGroup[p.group_id] = [];
                        groupedByGroup[p.group_id].push(p);
                    } else {
                        ungrouped.push(p);
                    }
                });
                
                // Прокси по группам
                for (const [gid, gProxies] of Object.entries(groupedByGroup)) {
                    const group = (groups || cachedProxyGroups || []).find(g => g.id === parseInt(gid));
                    const groupLabel = group ? group.name : `Группа ${gid}`;
                    html += `<optgroup label="📁 ${groupLabel}">`;
                    gProxies.forEach(p => {
                        const label = p.name || `${p.host}:${p.port}`;
                        const country = p.country || '';
                        const usedMark = p.accounts_count > 0 ? ' 👤' : '';
                        html += `<option value="${p.id}">${label}${country ? ' (' + country + ')' : ''}${usedMark}</option>`;
                    });
                    html += '</optgroup>';
                }
                
                // Прокси без группы
                if (ungrouped.length > 0) {
                    html += '<optgroup label="📍 Без группы">';
                    ungrouped.forEach(p => {
                        const label = p.name || `${p.host}:${p.port}`;
                        const country = p.country || '';
                        const usedMark = p.accounts_count > 0 ? ' 👤' : '';
                        html += `<option value="${p.id}">${label}${country ? ' (' + country + ')' : ''}${usedMark}</option>`;
                    });
                    html += '</optgroup>';
                }
            }
            
            return html;
        }
        
        async function showTdataUploadModal() {
            hideAllModals();
            tdataFile = null;
            
            // Сначала показываем модалку, потом подгружаем данные из кэша
            document.getElementById('tdataUploadModal').classList.add('show');
            
            // Сброс UI сразу
            document.getElementById('tdataFileInput').value = '';
            document.getElementById('tdataSelectedFile').style.display = 'none';
            document.getElementById('tdataDropZone').style.display = 'block';
            document.getElementById('tdataResults').style.display = 'none';
            document.getElementById('tdataResults').innerHTML = '';
            document.getElementById('tdataError').style.display = 'none';
            
            // Сбрасываем кнопку в исходное состояние
            const uploadBtn = document.getElementById('tdataUploadBtn');
            uploadBtn.textContent = 'Загрузить';
            uploadBtn.disabled = true;
            uploadBtn.style.opacity = '0.5';
            uploadBtn.onclick = uploadTdata;
            
            document.getElementById('tdataSkipCheck').checked = false;
            
            // Используем глобальный кэш
            await ensureDataCacheLoaded();
            
            // Заполняем селекты из кэша
            const proxySel = document.getElementById('tdataProxySelect');
            proxySel.innerHTML = generateProxyOptionsHtml(cachedProxies, cachedProxyGroups, true);
            
            const projectSel = document.getElementById('tdataProjectSelect');
            projectSel.innerHTML = '<option value="">Без проекта</option>' + cachedProjects.map(p => 
                `<option value="${p.id}">${p.name}</option>`
            ).join('');
            
            // Drag and drop
            const dropZone = document.getElementById('tdataDropZone');
            dropZone.ondragover = (e) => { e.preventDefault(); dropZone.style.borderColor = 'var(--primary)'; dropZone.style.background = 'var(--bg-hover)'; };
            dropZone.ondragleave = () => { dropZone.style.borderColor = 'var(--border)'; dropZone.style.background = 'transparent'; };
            dropZone.ondrop = (e) => {
                e.preventDefault();
                dropZone.style.borderColor = 'var(--border)';
                dropZone.style.background = 'transparent';
                const files = e.dataTransfer.files;
                if (files.length > 0) {
                    handleTdataFile(files[0]);
                }
            };
        }
        
        function onTdataFileSelected(input) {
            if (input.files.length > 0) {
                handleTdataFile(input.files[0]);
            }
        }
        
        function handleTdataFile(file) {
            if (!file.name.toLowerCase().endsWith('.zip')) {
                showError('tdataError', 'Файл должен быть ZIP архивом');
                return;
            }
            if (file.size > 200 * 1024 * 1024) {
                showError('tdataError', 'Файл слишком большой (макс 200MB)');
                return;
            }
            
            tdataFile = file;
            document.getElementById('tdataFileName').textContent = file.name;
            document.getElementById('tdataFileSize').textContent = formatFileSize(file.size);
            document.getElementById('tdataSelectedFile').style.display = 'block';
            document.getElementById('tdataDropZone').style.display = 'none';
            document.getElementById('tdataUploadBtn').disabled = false;
            document.getElementById('tdataUploadBtn').style.opacity = '1';
            document.getElementById('tdataError').style.display = 'none';
        }
        
        function clearTdataFile() {
            tdataFile = null;
            document.getElementById('tdataFileInput').value = '';
            document.getElementById('tdataSelectedFile').style.display = 'none';
            document.getElementById('tdataDropZone').style.display = 'block';
            document.getElementById('tdataUploadBtn').disabled = true;
            document.getElementById('tdataUploadBtn').style.opacity = '0.5';
        }
        
        function formatFileSize(bytes) {
            if (bytes < 1024) return bytes + ' B';
            if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
            return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
        }
        
        async function uploadTdata() {
            if (!tdataFile) {
                showError('tdataError', 'Выберите ZIP файл');
                return;
            }
            
            const token = localStorage.getItem('token');
            const proxyValue = document.getElementById('tdataProxySelect').value;
            const projectId = document.getElementById('tdataProjectSelect').value;
            const skipCheck = document.getElementById('tdataSkipCheck').checked;
            
            const btn = document.getElementById('tdataUploadBtn');
            const origText = btn.textContent;
            btn.textContent = '⏳ Загрузка и конвертация...';
            btn.disabled = true;
            
            document.getElementById('tdataError').style.display = 'none';
            document.getElementById('tdataResults').style.display = 'none';
            
            const formData = new FormData();
            formData.append('tdata_file', tdataFile);
            
            // Проверяем: група для авто-назначения или конкретный прокси
            if (proxyValue) {
                if (proxyValue.startsWith('group:')) {
                    const groupId = proxyValue.replace('group:', '');
                    formData.append('proxy_group_id', groupId);
                } else {
                    formData.append('proxy_id', proxyValue);
                }
            }
            
            if (projectId) formData.append('project_id', projectId);
            if (skipCheck) formData.append('skip_check', 'true');
            
            try {
                console.log('[TDATA] Sending upload request...', {
                    url: `${API_URL}/accounts/upload-tdata`,
                    fileSize: tdataFile.size,
                    fileName: tdataFile.name,
                    proxyValue, projectId, skipCheck
                });
                const res = await fetch(`${API_URL}/accounts/upload-tdata`, {
                    method: 'POST',
                    headers: { 'Authorization': `Bearer ${token}` },
                    body: formData
                });
                console.log('[TDATA] Response:', res.status, res.statusText);
                
                const data = await parseApiResponse(res);
                
                if (!res.ok) {
                    throw new Error(getErrorMessage(data, 'Ошибка загрузки tdata'));
                }
                
                // Показываем результаты
                const resultsDiv = document.getElementById('tdataResults');
                resultsDiv.style.display = 'block';
                
                let html = `<div style="padding:8px;font-size:0.85rem;border-bottom:1px solid var(--border);margin-bottom:8px;">
                    <strong>${data.message}</strong>
                </div>`;
                
                const successAccountIds = [];
                let hasChecking = false;
                for (const acc of data.accounts) {
                    const isChecking = acc.success && acc.status === 'checking';
                    const isDead = acc.success && acc.status === 'dead';
                    const icon = acc.success ? (isChecking ? '⏳' : (isDead ? '⚠️' : '✅')) : '❌';
                    const statusText = acc.success ? (isChecking ? 'проверка...' : (acc.status || 'active')) : (acc.error || 'Ошибка');
                    const statusColor = isChecking ? 'var(--primary)' : (isDead ? 'var(--warning, #f59e0b)' : (acc.success ? 'var(--success)' : 'var(--error)'));
                    html += `<div style="padding:6px 8px;font-size:0.8rem;display:flex;align-items:center;gap:8px;border-bottom:1px solid var(--border);">
                        <span>${icon}</span>
                        <span style="flex:1;overflow:hidden;text-overflow:ellipsis;">${acc.phone || 'Аккаунт ' + acc.index}</span>
                        <span style="color:${statusColor};">${statusText}</span>
                    </div>`;
                    if (acc.success && acc.account_id) {
                        successAccountIds.push(acc.account_id);
                    }
                    if (isChecking) hasChecking = true;
                }
                
                // Добавляем инфо если есть аккаунты на проверке
                if (hasChecking) {
                    html += `<div style="padding:10px 8px;font-size:0.8rem;color:var(--text-secondary);background:var(--bg-hover);border-radius:0 0 8px 8px;">
                        ℹ️ Статус обновится автоматически через 10-20 сек. Аккаунты доступны в Менеджере аккаунтов.
                    </div>`;
                }
                
                resultsDiv.innerHTML = html;
                
                // Обновляем список аккаунтов
                if (data.success_count > 0) {
                    loadAccounts();
                    // Проверка теперь делается через Celery в фоне
                }
                
                // Кнопка "Закрыть" вместо "Загрузить"
                btn.textContent = '✓ Закрыть';
                btn.disabled = false;
                btn.onclick = () => hideAllModals();
                
            } catch (err) {
                console.error('[TDATA] Error:', err.name, err.message, err);
                showError('tdataError', err.message);
                btn.textContent = origText;
                btn.disabled = false;
            }
        }

        // ============================================================================
        // Broadcast (Рассылка) Functions
        // ============================================================================

        let broadcastContentType = 'text';
        let broadcastSource = 'parsed';
        let broadcastFileUsers = null;
        async function fetchWithTimeout(url, options = {}, timeoutMs = 15000) {
            const ctrl = new AbortController();
            const timer = setTimeout(() => ctrl.abort(), timeoutMs);
            try {
                return await fetch(url, { ...options, signal: ctrl.signal });
            } finally {
                clearTimeout(timer);
            }
        }

        let currentBroadcastJobId = null;
        let broadcastDetailRefresh = null;
        let editingBroadcastJobId = null;
        let editingOriginalParsedBaseId = null;
        let bcAccountPicker = null;

        function showCreateBroadcastModal(isEdit = false) {
            const modal = document.getElementById('createBroadcastModal');
            const title = modal.querySelector('h2');
            const submitBtn = document.getElementById('bcSubmitBtn');
            
            if (isEdit && editingBroadcastJobId) {
                title.textContent = '✏️ Редактировать рассылку';
                submitBtn.textContent = '💾 Сохранить изменения';
                submitBtn.setAttribute('onclick', 'saveBroadcastJob()');
            } else {
                editingBroadcastJobId = null;
                editingOriginalParsedBaseId = null;
                broadcastFileUsers = null;
                broadcastPhonesFileUsers = null;
                title.textContent = '📢 Создать рассылку';
                submitBtn.textContent = '🚀 Создать задачу рассылки';
                submitBtn.setAttribute('onclick', 'createBroadcastJob()');
                // Очищаем все поля при создании новой
                document.getElementById('bcName').value = '';
                document.getElementById('bcMessageText').value = '';
                document.getElementById('bcPostLinksContainer').innerHTML = '';
                addPostLinkRow();
                document.getElementById('bcSpintax').checked = true;
                document.getElementById('bcDeleteAfterSend').checked = false;
                document.getElementById('bcSilentSend').checked = false;
                document.getElementById('bcHideAuthor').checked = false;
                document.getElementById('bcPauseMin').value = '15';
                document.getElementById('bcPauseMax').value = '30';
                document.getElementById('bcBatchSize').value = '3';
                document.getElementById('bcDailyLimit').value = '0';
                document.getElementById('bcAutoStop').value = '0';
                document.getElementById('bcParsedBase').value = '';
                document.getElementById('bcManualUsers').value = '';
                if (document.getElementById('bcPhonesManual')) document.getElementById('bcPhonesManual').value = '';
                if (document.getElementById('bcPhonesFileInput')) document.getElementById('bcPhonesFileInput').value = '';
                if (document.getElementById('bcPhonesFileInfo')) document.getElementById('bcPhonesFileInfo').textContent = '';
                if (document.getElementById('bcPhonesManualCount')) document.getElementById('bcPhonesManualCount').textContent = '';
                setBroadcastContentType('text');
                setBroadcastSource('parsed');
            }
            
            modal.classList.add('show');
            loadBroadcastParserBases();
            loadBroadcastAccounts();
        }

        async function editBroadcastJob(jobId) {
            try {
                const res = await fetch(`${API_URL}/broadcast/jobs/${jobId}`, {
                    headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` }
                });
                if (!res.ok) throw new Error('Ошибка загрузки задачи');
                const data = await res.json();
                const job = data.job;
                
                if (job.status === 'running') {
                    showNotification('Нельзя редактировать запущенную задачу. Сначала поставьте на паузу.', 'error');
                    return;
                }
                
                editingBroadcastJobId = jobId;
                broadcastFileUsers = null; // Сбрасываем файл от предыдущих операций
                broadcastPhonesFileUsers = null; // Сбрасываем phones-файл от предыдущих операций
                editingOriginalParsedBaseId = job.parsed_base_id || null;
                
                // Очищаем UI от предыдущих файлов
                const fileInfo = document.getElementById('bcFileInfo');
                if (fileInfo) fileInfo.textContent = '';
                const fileInput = document.getElementById('bcFileInput');
                if (fileInput) fileInput.value = '';
                const phonesFileInfo = document.getElementById('bcPhonesFileInfo');
                if (phonesFileInfo) phonesFileInfo.textContent = '';
                const phonesFileInput = document.getElementById('bcPhonesFileInput');
                if (phonesFileInput) phonesFileInput.value = '';
                
                // Сбрасываем _originalText чтобы кнопка "Оригинал" работала корректно
                _originalText = job.message_text || '';
                
                // Заполняем поля
                document.getElementById('bcName').value = job.name || '';
                document.getElementById('bcMessageText').value = job.message_text || '';
                // Populate post links
                const plContainer = document.getElementById('bcPostLinksContainer');
                plContainer.innerHTML = '';
                let postLinksArr = job.post_links;
                if (typeof postLinksArr === 'string') { try { postLinksArr = JSON.parse(postLinksArr); } catch(e) { postLinksArr = null; } }
                if (Array.isArray(postLinksArr) && postLinksArr.length > 0) {
                    postLinksArr.forEach(pl => addPostLinkRow(pl.url, pl.limit));
                } else if (job.post_link) {
                    addPostLinkRow(job.post_link, 0);
                } else {
                    addPostLinkRow();
                }
                document.getElementById('bcSpintax').checked = job.spintax_enabled !== false;
                document.getElementById('bcDeleteAfterSend').checked = job.delete_after_send === true;
                document.getElementById('bcSilentSend').checked = job.silent_send === true;
                document.getElementById('bcHideAuthor').checked = job.hide_author === true;
                document.getElementById('bcPauseMin').value = job.pause_min != null ? job.pause_min : '15';
                document.getElementById('bcPauseMax').value = job.pause_max != null ? job.pause_max : '30';
                document.getElementById('bcBatchSize').value = job.batch_size != null ? job.batch_size : '1';
                document.getElementById('bcDailyLimit').value = job.daily_limit != null ? job.daily_limit : '0';
                document.getElementById('bcAutoStop').value = job.auto_stop_percent != null ? job.auto_stop_percent : '50';
                
                // Тип контента
                setBroadcastContentType(job.content_type || 'text');
                
                // Источник получателей
                const qs = data.queue_stats;
                const recipientInfo = `Текущее состояние: ${qs.pending} в очереди, ${qs.sent} отправлено, ${qs.failed} ошибок, ${qs.skipped} пропущено`;
                if (job.users_source === 'parsed_base' && job.parsed_base_id) {
                    setBroadcastSource('parsed');
                } else if (job.users_source === 'phones') {
                    setBroadcastSource('phones');
                    setBcPhonesMode('text');
                    document.getElementById('bcPhonesManual').value = `(номера уже загружены — ${recipientInfo})\n(оставьте как есть или введите новых)`;
                } else {
                    setBroadcastSource('manual');
                    document.getElementById('bcManualUsers').value = `(получатели уже загружены — ${recipientInfo})\n(оставьте поле как есть или введите новых ниже)`;
                }
                
                // Открываем модал — это вызовет loadBroadcastParserBases и loadBroadcastAccounts
                showCreateBroadcastModal(true);
                
                // Выбираем аккаунты и базу после загрузки
                const accountIds = job.account_ids || [];
                const parsedBaseId = job.parsed_base_id;
                
                // Ждём загрузки AccountPicker, потом выставляем значения
                let waitAttempts = 0;
                const waitAndSelect = () => {
                    waitAttempts++;
                    if ((!bcAccountPicker || bcAccountPicker.accounts.length === 0) && waitAttempts < 30) {
                        setTimeout(waitAndSelect, 300);
                        return;
                    }
                    if (bcAccountPicker && accountIds.length) {
                        bcAccountPicker.setSelection(new Set(accountIds));
                    }
                    
                    // Выбираем парсерную базу если есть
                    if (parsedBaseId) {
                        const select = document.getElementById('bcParsedBase');
                        if (select) {
                            select.value = parsedBaseId;
                            if (!select.value) {
                                // Опция ещё не загрузилась, ждём
                                setTimeout(() => { select.value = parsedBaseId; }, 500);
                            }
                        }
                    }
                };
                setTimeout(waitAndSelect, 300);
                
            } catch (e) {
                showNotification(e.message, 'error');
            }
        }

        async function saveBroadcastJob() {
            if (!editingBroadcastJobId) return;
            
            const contentType = broadcastContentType;
            const messageText = document.getElementById('bcMessageText').value.trim();

            if (contentType === 'text' && !messageText) {
                showNotification('Укажите текст сообщения', 'error');
                return;
            }
            if (contentType === 'post') {
                const postLinks = getPostLinksData();
                if (!postLinks.length) {
                    showNotification('Добавьте хотя бы одну ссылку на пост', 'error');
                    return;
                }
            }

            const pauseMin = parseInt(document.getElementById('bcPauseMin').value) || 15;
            const pauseMax = parseInt(document.getElementById('bcPauseMax').value) || 30;
            if (pauseMin > pauseMax) {
                showNotification('Минимальная пауза не может быть больше максимальной', 'error');
                return;
            }

            const accountIds = bcAccountPicker ? Array.from(bcAccountPicker.getSelected()) : [];
            if (accountIds.length === 0) {
                showNotification('Выберите хотя бы один аккаунт', 'error');
                return;
            }

            const body = {
                name: document.getElementById('bcName').value.trim() || null,
                content_type: contentType,
                message_text: contentType === 'text' ? messageText : null,
                spintax_enabled: document.getElementById('bcSpintax').checked,
                delete_after_send: document.getElementById('bcDeleteAfterSend').checked,
                silent_send: document.getElementById('bcSilentSend')?.checked || false,
                hide_author: document.getElementById('bcHideAuthor')?.checked || false,
                pause_min: pauseMin,
                pause_max: pauseMax,
                batch_size: parseInt(document.getElementById('bcBatchSize').value) || 1,
                daily_limit: parseInt(document.getElementById('bcDailyLimit').value) || 0,
                auto_stop_percent: parseInt(document.getElementById('bcAutoStop').value) || 0,
                account_ids: accountIds
            };

            // Post links
            if (contentType === 'post') {
                const postLinks = getPostLinksData();
                body.post_links = postLinks;
                body.post_link = postLinks.length > 0 ? postLinks[0].url : null;
            } else {
                body.post_link = null;
                body.post_links = null;
            }

            // Получатели - только если явно указаны новые
            if (broadcastSource === 'parsed') {
                const parsedBaseId = parseInt(document.getElementById('bcParsedBase').value);
                if (parsedBaseId) {
                    // Отправляем parsed_base_id только если база изменилась (чтобы не сбросить прогресс)
                    if (!editingBroadcastJobId || parsedBaseId !== editingOriginalParsedBaseId) {
                        body.parsed_base_id = parsedBaseId;
                    }
                }
            } else if (broadcastSource === 'file' && broadcastFileUsers && broadcastFileUsers.length > 0) {
                body.users_data = broadcastFileUsers;
            } else if (broadcastSource === 'phones') {
                if (bcPhonesMode === 'file' && broadcastPhonesFileUsers && broadcastPhonesFileUsers.length > 0) {
                    body.users_data = broadcastPhonesFileUsers;
                } else if (bcPhonesMode === 'text') {
                    let phonesText = document.getElementById('bcPhonesManual').value.trim();
                    phonesText = phonesText.split('\n').filter(l => !l.trim().startsWith('(номера уже загружены') && !l.trim().startsWith('(оставьте как есть')).join('\n').trim();
                    if (phonesText) {
                        const phoneUsers = parsePhoneLines(phonesText);
                        if (phoneUsers.length > 0) {
                            body.users_data = phoneUsers;
                        }
                    }
                }
            } else if (broadcastSource === 'manual') {
                let manualText = document.getElementById('bcManualUsers').value.trim();
                // Убираем строки-плейсхолдеры
                manualText = manualText.split('\n').filter(l => !l.trim().startsWith('(получатели уже загружены') && !l.trim().startsWith('(оставьте поле как есть')).join('\n').trim();
                if (manualText) {
                    const users = parseManualUsers(manualText);
                    if (users.length > 0) {
                        body.users_data = users;
                    }
                }
            }

            try {
                const res = await fetch(`${API_URL}/broadcast/jobs/${editingBroadcastJobId}`, {
                    method: 'PUT',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${localStorage.getItem('token')}`
                    },
                    body: JSON.stringify(body)
                });
                
                const data = await res.json();
                if (!res.ok) throw new Error(data.detail || 'Ошибка обновления');
                
                showNotification('✅ Задача обновлена', 'success');
                closeBroadcastModal();
                loadBroadcastJobs();
                editingBroadcastJobId = null;
            } catch (e) {
                showNotification(e.message, 'error');
            }
        }

        function parseManualUsers(text) {
            const lines = text.split('\n').filter(l => l.trim());
            return lines.map(line => {
                line = line.trim().replace(/^@/, '');
                return /^\d+$/.test(line) ? { user_id: parseInt(line) } : { username: line };
            });
        }

        function closeBroadcastModal() {
            document.getElementById('createBroadcastModal').classList.remove('show');
            editingBroadcastJobId = null;
            editingOriginalParsedBaseId = null;
            broadcastFileUsers = null;
            if (bcAccountPicker) { bcAccountPicker.destroy(); bcAccountPicker = null; }
        }