// =============================================================
// HYPER ANALYTICS — концепт "Ink & Amber" (SPA, hash-роутер)
// Прототип: данные из mock.js, изменяющие действия показывают
// тост «в прототипе действие не выполняется».
// =============================================================
window.UI = (function () {
    'use strict';
    const M = window.MOCK;
    const $ = (s, r) => (r || document).querySelector(s);
    const $$ = (s, r) => Array.from((r || document).querySelectorAll(s));

    // ---------- брейкпоинт: десктоп ----------
    // Мобильная разметка — основная; десктопные варианты экранов
    // включаются этим флагом и перерисовываются при смене ширины.
    const mqDesktop = window.matchMedia('(min-width: 1000px)');
    const isDesktop = () => mqDesktop.matches;
    (mqDesktop.addEventListener ? mqDesktop.addEventListener.bind(mqDesktop) : mqDesktop.addListener.bind(mqDesktop))('change', () => render());

    // ---------- форматтеры ----------
    const esc = s => String(s == null ? '' : s).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
    const usd = (v, sign) => {
        if (v == null || isNaN(v)) return '—';
        const a = Math.abs(v);
        const s = a >= 1000 ? a.toLocaleString('ru-RU', { maximumFractionDigits: 0 }) : a.toFixed(2);
        return (v < 0 ? '−' : sign ? '+' : '') + '$' + s;
    };
    const pct = (v, sign = true) => v == null || isNaN(v) ? '—' : (v < 0 ? '−' : sign ? '+' : '') + Math.abs(v).toFixed(2) + '%';
    const num = v => v == null ? '—' : (+v).toLocaleString('ru-RU');
    const price = v => v == null ? '—' : (+v < 0.01 ? (+v).toPrecision(3) : (+v).toLocaleString('ru-RU', { maximumFractionDigits: 2 }));
    const shortAddr = a => a ? a.slice(0, 6) + '…' + a.slice(-4) : '';
    const pnlCls = v => v > 0 ? 'up-t' : v < 0 ? 'down-t' : 'mut-t';
    const age = min => min < 60 ? min + ' мин' : min < 1440 ? Math.floor(min / 60) + ' ч ' + (min % 60) + ' м' : Math.floor(min / 1440) + ' д ' + Math.floor(min % 1440 / 60) + ' ч';
    const relTime = str => {
        const d = new Date(str.replace(' ', 'T'));
        const diff = (Date.now() - d.getTime()) / 60000;
        if (isNaN(diff)) return str;
        if (diff < 1) return 'только что';
        if (diff < 60) return Math.floor(diff) + ' мин назад';
        if (diff < 1440) return Math.floor(diff / 60) + ' ч назад';
        return Math.floor(diff / 1440) + ' дн назад';
    };

    // ---------- бейджи и ссылки ----------
    const statusBadge = st => {
        const map = { ACTIVE: ['b-active', 'ACTIVE'], TEST: ['b-test', 'TEST'], WATCH: ['b-watch', 'WATCH'], ARCHIVE: ['b-archive', 'ARCHIVE'] };
        const [cls, t] = map[st] || ['b-off', st];
        return `<span class="badge ${cls}">${t}</span>`;
    };
    const sideBadge = s => `<span class="badge ${s === 'long' ? 'b-long' : 'b-short'}">${s === 'long' ? '▲ long' : '▼ short'}</span>`;
    const reasonBadge = r => r === 'liquidation' ? '<span class="badge b-liq">LIQ</span>' : r === 'partial' ? '<span class="badge b-part">PART</span>' : '';
    const modeBadge = m => ({
        open: '<span class="badge b-long">open</span>', close: '<span class="badge b-short">close</span>',
        watch: '<span class="badge b-watch">watch</span>', partial_close: '<span class="badge b-part">partial</span>',
    }[m] || `<span class="badge b-off">${esc(m)}</span>`);
    const gradeBadge = g => `<span class="grade g-${g === '?' ? 'Q' : g}">${g}</span>`;
    const stars = r => { let h = ''; for (let i = 1; i <= 5; i++) h += `<span${i <= r ? '' : ' class="dim"'}>★</span>`; return `<span class="stars">${h}</span>`; };
    const tagPill = t => `<span class="tagp">${esc(t)}</span>`;
    const extIco = '<svg viewBox="0 0 16 16" width="11" height="11" fill="none" stroke="currentColor" stroke-width="1.6"><path d="M6 3h7v7M13 3 7 9M11 9v4H3V5h4"/></svg>';
    const hdLink = addr => `<a class="link-ico" href="https://hyperdash.info/trader/${esc(addr)}" target="_blank" rel="noopener" onclick="event.stopPropagation()">HD ${extIco}</a>`;
    const tvLink = sym => `<a class="link-ico" href="https://www.tradingview.com/chart/?symbol=${esc(sym)}USDT.P" target="_blank" rel="noopener" onclick="event.stopPropagation()">TV ${extIco}</a>`;

    // ---------- SVG-графики (самописные, без зависимостей) ----------
    function spark(vals, w = 130, h = 44, opts = {}) {
        if (!vals || !vals.length) return '';
        const min = Math.min(0, ...vals), max = Math.max(0, ...vals), span = max - min || 1;
        const px = i => (i / (vals.length - 1)) * w;
        const py = v => h - 4 - ((v - min) / span) * (h - 8);
        const pts = vals.map((v, i) => px(i).toFixed(1) + ',' + py(v).toFixed(1)).join(' ');
        const sum = vals.reduce((s, v) => s + v, 0);
        const col = opts.color || (sum >= 0 ? 'var(--up)' : 'var(--down)');
        const gid = 'sg' + Math.random().toString(36).slice(2, 8);
        return `<svg viewBox="0 0 ${w} ${h}" preserveAspectRatio="none" style="width:100%;height:100%">
            <defs><linearGradient id="${gid}" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" stop-color="${col}" stop-opacity=".35"/><stop offset="1" stop-color="${col}" stop-opacity="0"/></linearGradient></defs>
            <polygon points="0,${h} ${pts} ${w},${h}" fill="url(#${gid})"/>
            <polyline points="${pts}" fill="none" stroke="${col}" stroke-width="1.8" stroke-linejoin="round" stroke-linecap="round"/></svg>`;
    }

    function areaChart(vals, labels, h = 170, opts = {}) {
        const w = 360, padL = opts.noAxis ? 0 : 34, padB = labels ? 18 : 6, padT = 10;
        const min = Math.min(opts.zero === false ? Infinity : 0, ...vals), max = Math.max(0, ...vals), span = (max - min) || 1;
        const iw = w - padL - 6, ih = h - padT - padB;
        const px = i => padL + (i / (vals.length - 1)) * iw;
        const py = v => padT + ih - ((v - min) / span) * ih;
        const pts = vals.map((v, i) => px(i).toFixed(1) + ',' + py(v).toFixed(1)).join(' ');
        const col = opts.color || 'var(--brand)';
        const gid = 'ag' + Math.random().toString(36).slice(2, 8);
        let axis = '';
        if (!opts.noAxis) {
            for (let k = 0; k <= 3; k++) {
                const v = min + span * k / 3, y = py(v);
                axis += `<line x1="${padL}" x2="${w - 4}" y1="${y}" y2="${y}" stroke="var(--line)" stroke-width="1"/>
                         <text class="axis-l" x="${padL - 5}" y="${y + 3}" text-anchor="end">${Math.round(v)}</text>`;
            }
        }
        let xl = '';
        if (labels) {
            const step = Math.ceil(labels.length / 5);
            labels.forEach((l, i) => { if (i % step === 0 || i === labels.length - 1) xl += `<text class="axis-l" x="${px(i)}" y="${h - 4}" text-anchor="middle">${esc(l)}</text>`; });
        }
        return `<svg class="chart-svg" viewBox="0 0 ${w} ${h}">
            <defs><linearGradient id="${gid}" x1="0" y1="0" x2="0" y2="1">
                <stop offset="0" stop-color="${col}" stop-opacity=".32"/><stop offset="1" stop-color="${col}" stop-opacity="0"/></linearGradient></defs>
            ${axis}
            <polygon points="${px(0)},${py(min)} ${pts} ${px(vals.length - 1)},${py(min)}" fill="url(#${gid})"/>
            <polyline points="${pts}" fill="none" stroke="${col}" stroke-width="2" stroke-linejoin="round"/>
            <circle cx="${px(vals.length - 1)}" cy="${py(vals[vals.length - 1])}" r="3.5" fill="${col}"/>
            ${xl}</svg>`;
    }

    function barChart(vals, labels, h = 170) {
        const w = 360, padL = 34, padB = 18, padT = 8;
        const min = Math.min(0, ...vals), max = Math.max(0, ...vals), span = (max - min) || 1;
        const iw = w - padL - 6, ih = h - padT - padB;
        const py = v => padT + ih - ((v - min) / span) * ih;
        const y0 = py(0), bw = Math.min(18, iw / vals.length * 0.62);
        let bars = '', axis = '', xl = '';
        for (let k = 0; k <= 3; k++) {
            const v = min + span * k / 3, y = py(v);
            axis += `<line x1="${padL}" x2="${w - 4}" y1="${y}" y2="${y}" stroke="var(--line)"/>
                     <text class="axis-l" x="${padL - 5}" y="${y + 3}" text-anchor="end">${Math.round(v)}</text>`;
        }
        vals.forEach((v, i) => {
            const x = padL + (i + .5) / vals.length * iw - bw / 2;
            const y = Math.min(y0, py(v)), bh = Math.max(2, Math.abs(py(v) - y0));
            bars += `<rect x="${x.toFixed(1)}" y="${y.toFixed(1)}" width="${bw.toFixed(1)}" height="${bh.toFixed(1)}" rx="3.5"
                     fill="${v >= 0 ? 'var(--up)' : 'var(--down)'}" opacity=".9"><title>${labels ? esc(labels[i]) + ': ' : ''}${v}</title></rect>`;
        });
        if (labels) {
            const step = Math.ceil(labels.length / 6);
            labels.forEach((l, i) => { if (i % step === 0) xl += `<text class="axis-l" x="${padL + (i + .5) / vals.length * iw}" y="${h - 4}" text-anchor="middle">${esc(l)}</text>`; });
        }
        return `<svg class="chart-svg" viewBox="0 0 ${w} ${h}">${axis}${bars}<line x1="${padL}" x2="${w - 4}" y1="${y0}" y2="${y0}" stroke="var(--line-strong)"/>${xl}</svg>`;
    }

    function hBars(rows, opts = {}) {
        const maxAbs = Math.max(...rows.map(r => Math.abs(r.value))) || 1;
        return rows.map(r => {
            const wPct = Math.max(3, Math.abs(r.value) / maxAbs * 100);
            const col = opts.neutral ? 'var(--info)' : (r.value >= 0 ? 'var(--up)' : 'var(--down)');
            return `<div style="display:flex;align-items:center;gap:10px;margin-bottom:9px">
                <div class="mono small" style="width:52px;flex:none;display:flex;gap:4px;align-items:center">${r.labelHtml || esc(r.label)}</div>
                <div style="flex:1;height:14px;border-radius:7px;background:var(--srf-2);overflow:hidden">
                    <div style="width:${wPct}%;height:100%;border-radius:7px;background:${col};opacity:.85"></div></div>
                <div class="mono small ${opts.neutral ? '' : pnlCls(r.value)}" style="width:64px;flex:none;text-align:right">${r.valueText != null ? r.valueText : r.value}</div>
            </div>`;
        }).join('');
    }

    // ---------- тосты и sheets ----------
    function toast(msg) {
        let wrap = $('.toast-wrap');
        if (!wrap) { wrap = document.createElement('div'); wrap.className = 'toast-wrap'; document.body.appendChild(wrap); }
        const t = document.createElement('div');
        t.className = 'toast'; t.textContent = msg;
        wrap.appendChild(t);
        setTimeout(() => { t.classList.add('out'); setTimeout(() => t.remove(), 320); }, 2600);
    }
    const stub = () => toast('Прототип: действие не выполняется и ничего не сохраняет');

    function openSheet(title, bodyHtml, onMount) {
        closeSheet(true);
        const bd = document.createElement('div'); bd.className = 'sheet-backdrop'; bd.onclick = () => closeSheet();
        const sh = document.createElement('div'); sh.className = 'sheet';
        sh.innerHTML = `<div class="sh-grip"></div>
            <div class="sh-head"><div class="sh-title">${esc(title)}</div>
            <button class="icon-btn" onclick="UI.closeSheet()" aria-label="Закрыть"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 4l8 8M12 4l-8 8"/></svg></button></div>
            <div class="sh-body">${bodyHtml}</div>`;
        document.body.append(bd, sh);
        requestAnimationFrame(() => requestAnimationFrame(() => document.body.classList.add('sheet-open')));
        if (onMount) onMount(sh);
    }
    function closeSheet(instant) {
        document.body.classList.remove('sheet-open');
        const rm = () => { $$('.sheet, .sheet-backdrop').forEach(e => e.remove()); };
        instant ? rm() : setTimeout(rm, 330);
    }

    // ---------- каркас ----------
    const ICONS = {
        home: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M3 11.5 12 4l9 7.5M5.5 10v9h13v-9"/></svg>',
        traders: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="9" cy="8" r="3.4"/><path d="M3.5 19c.6-3 2.9-4.6 5.5-4.6s4.9 1.6 5.5 4.6M15.5 5.2a3.4 3.4 0 0 1 0 5.6M17.5 14.6c1.7.7 2.7 2.2 3 4.4"/></svg>',
        positions: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 17l4.5-5 3.5 3 4-6 4 4.5"/><path d="M4 21h16"/></svg>',
        journal: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="5" y="3.5" width="14" height="17" rx="2.5"/><path d="M9 8.5h6M9 12h6M9 15.5h3.5"/></svg>',
        more: '<svg viewBox="0 0 24 24" fill="currentColor"><circle cx="5" cy="12" r="2"/><circle cx="12" cy="12" r="2"/><circle cx="19" cy="12" r="2"/></svg>',
        analytics: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M5 20V10M12 20V4M19 20v-7"/></svg>',
        pools: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="8" cy="9" r="4"/><circle cx="16" cy="15" r="4"/></svg>',
        bulk: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="4" y="4" width="7" height="7" rx="2"/><rect x="13" y="4" width="7" height="7" rx="2"/><rect x="4" y="13" width="7" height="7" rx="2"/><path d="M16.5 13.5v6M13.5 16.5h6"/></svg>',
        settings: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="3"/><path d="M12 3v2.5M12 18.5V21M21 12h-2.5M5.5 12H3M18.4 5.6l-1.8 1.8M7.4 16.6l-1.8 1.8M18.4 18.4l-1.8-1.8M7.4 7.4 5.6 5.6"/></svg>',
        logout: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 4h-8v16h8M10 12h11M18 8.5 21.5 12 18 15.5"/></svg>',
        back: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.5 5 8 12l6.5 7"/></svg>',
        plus: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2"><path d="M12 5v14M5 12h14"/></svg>',
        search: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="11" cy="11" r="6.5"/><path d="m16 16 4.5 4.5"/></svg>',
        filter: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 6h16M7 12h10M10 18h4"/></svg>',
        download: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 4v11M7.5 11.5 12 16l4.5-4.5M5 20h14"/></svg>',
    };
    const DOCK = [
        { href: '#/', ico: 'home', label: 'Обзор', match: h => h === '#/' || h === '' || h === '#' },
        { href: '#/traders', ico: 'traders', label: 'Трейдеры', match: h => h.startsWith('#/trader') },
        { href: '#/positions', ico: 'positions', label: 'Позиции', match: h => h.startsWith('#/positions') },
        { href: '#/journal', ico: 'journal', label: 'Журнал', match: h => h.startsWith('#/journal') },
    ];
    const MORE = [
        { href: '#/analytics', ico: 'analytics', label: 'Аналитика' },
        { href: '#/pools', ico: 'pools', label: 'Пулы' },
        { href: '#/bulk', ico: 'bulk', label: 'Массовые операции' },
        { href: '#/settings', ico: 'settings', label: 'Настройки' },
        { href: '#/login', ico: 'logout', label: 'Выйти' },
    ];

    function moreSheet() {
        const h = location.hash;
        openSheet('Ещё', MORE.map(m =>
            `<a class="row-card" href="${m.href}" onclick="UI.closeSheet()" style="text-decoration:none;color:inherit${MORE.slice(0, 4).some(x => h.startsWith(x.href)) && h.startsWith(m.href) ? ';border-color:rgba(245,185,66,.45)' : ''}">
                <span class="icon-btn" style="pointer-events:none">${ICONS[m.ico]}</span>
                <div class="rc-main"><div class="rc-title">${m.label}</div></div>
                <span class="muted">›</span></a>`).join('') +
            `<div class="small muted" style="text-align:center;margin-top:10px">Hyper Analytics · концепт UX/UI · данные демонстрационные</div>`);
    }

    function shell(opts) {
        const h = location.hash || '#/';
        const moreOn = MORE.slice(0, 4).some(m => h.startsWith(m.href));
        let dock;
        if (isDesktop()) {
            // Десктоп: вертикальный рейл со всеми разделами, «Ещё» не нужно
            const items = [...DOCK, ...MORE.map(m => ({ ...m, match: x => x.startsWith(m.href) }))];
            dock = `<nav class="dock dock-rail">
                <div class="dock-brand"><div class="brand-dot">H</div><span>Hyper<br>Analytics</span></div>` +
                items.map(d => `<a href="${d.href}" class="${d.match(h) ? 'on' : ''}">${ICONS[d.ico]}<span>${d.label}</span></a>`).join('') +
                `</nav>`;
        } else {
            dock = `<nav class="dock">` + DOCK.map(d =>
                `<a href="${d.href}" class="${d.match(h) ? 'on' : ''}">${ICONS[d.ico]}<span>${d.label}</span></a>`).join('') +
                `<a href="javascript:void(0)" onclick="UI.moreSheet()" class="${moreOn ? 'on' : ''}">${ICONS.more}<span>Ещё</span></a></nav>`;
        }
        const left = opts.back
            ? `<button class="icon-btn" onclick="history.length>1?history.back():location.hash='${opts.back}'" aria-label="Назад">${ICONS.back}</button>`
            : `<div class="brand-dot">H</div>`;
        return `<div class="app">
            <header class="topbar">${left}
                <h1 class="tb-title">${esc(opts.title)}${opts.sub ? `<span class="tb-sub">${opts.sub}</span>` : ''}</h1>
                ${opts.actions || ''}
            </header>
            <main>${opts.body}</main>
        </div>${dock}${opts.fab || ''}`;
    }

    // ---------- вкладки / сегменты ----------
    function bindTabs(root) {
        $$('.tabs', root).forEach(tabs => {
            $$('button', tabs).forEach(btn => btn.onclick = () => {
                $$('button', tabs).forEach(b => b.classList.toggle('on', b === btn));
                const paneRoot = tabs.parentElement;
                $$(':scope > .tab-pane', paneRoot).forEach(p => p.classList.toggle('on', p.dataset.pane === btn.dataset.tab));
                btn.scrollIntoView({ block: 'nearest', inline: 'center', behavior: 'smooth' });
            });
        });
    }

    // ---------- сортируемый заголовок таблицы (десктоп) ----------
    // Клик по колонке ставит descKey; повторный клик — ascKey, если он есть.
    function thSort(label, cur, setExpr, descKey, ascKey, cls) {
        const on = cur === descKey || (ascKey && cur === ascKey);
        const next = (cur === descKey && ascKey) ? ascKey : descKey;
        const arrow = cur === descKey ? ' ↓' : (ascKey && cur === ascKey) ? ' ↑' : '';
        return `<th class="${on ? 'sort-on' : ''} ${cls || ''}" onclick="${setExpr}('${next}')">${label}${arrow}</th>`;
    }

    // ---------- CSV ----------
    function downloadCSV(name, rows) {
        const csv = rows.map(r => r.map(c => /[",;\n]/.test(String(c)) ? '"' + String(c).replace(/"/g, '""') + '"' : c).join(';')).join('\n');
        const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8' });
        const a = document.createElement('a');
        a.href = URL.createObjectURL(blob); a.download = name; a.click();
        URL.revokeObjectURL(a.href);
        toast('CSV выгружен: ' + name);
    }

    // ---------- роутер ----------
    const routes = {};
    function route(path, fn) { routes[path] = fn; }
    function render() {
        closeSheet(true);
        const hash = location.hash || '#/';
        const [path, param] = (() => {
            const p = hash.slice(2).split('/');
            if (p[0] === 'trader' && p[1]) return ['trader', p[1]];
            if (p[0] === 'pool' && p[1]) return ['pool', p[1]];
            return [p[0] || 'dashboard', null];
        })();
        const fn = routes[path] || routes.dashboard;
        document.body.classList.toggle('login-mode', path === 'login');
        const root = $('#root');
        root.innerHTML = fn(param);
        bindTabs(root);
        window.scrollTo(0, 0);
    }
    window.addEventListener('hashchange', render);
    document.addEventListener('DOMContentLoaded', render);
    // строки таблиц с data-href кликабельны (кроме кликов по ссылкам/контролам)
    document.addEventListener('click', e => {
        const tr = e.target.closest('tr[data-href]');
        if (tr && !e.target.closest('a, button, input, select, label')) location.hash = tr.dataset.href;
    });

    return {
        M, $, $$, esc, usd, pct, num, price, shortAddr, pnlCls, age, relTime,
        statusBadge, sideBadge, reasonBadge, modeBadge, gradeBadge, stars, tagPill, hdLink, tvLink,
        spark, areaChart, barChart, hBars,
        toast, stub, openSheet, closeSheet, moreSheet, shell, route, render, downloadCSV, ICONS,
        isDesktop, thSort,
    };
})();
