import React, { useState, useRef, useEffect, useCallback } from 'react';
import { Client, getStateCallbacks } from 'colyseus.js';
import { Link } from 'react-router-dom';
import { REALTIME_WS, REALTIME_HTTP } from '../api/API';
import { KT, KUBIKON_KEYFRAMES } from '../utils/kubikonTheme';
import RublocsLogo from '../components/RublocsLogo/RublocsLogo';
import Icon from '../editor/Icon';

/**
 * RealtimeTest — отладочная страница для подэтапа 4.1 мультиплеера.
 * URL: /kubikon-realtime-test
 *
 * Что делает:
 *   1. Кнопка «Подключиться» — создаёт colyseus.js Client и
 *      делает joinOrCreate('battle', { projectId, username }).
 *   2. Пишет в лог всё что прилетает: state.players add/remove,
 *      messages 'welcome', serverTime tick, errors.
 *   3. Кнопка «Отправить hello» шлёт сообщение на сервер,
 *      получаем 'welcome' обратно.
 *   4. Кнопка «Health» проверяет HTTP /health.
 *
 * Цель: убедиться что вся цепочка
 *   браузер → NPM (когда настроим) → VM 1598 → docker → Colyseus
 * работает.
 */
const RealtimeTest = () => {
    const [logs, setLogs] = useState([]);
    const [status, setStatus] = useState('disconnected');   // 'disconnected' | 'connecting' | 'connected'
    const [room, setRoom] = useState(null);
    const [players, setPlayers] = useState([]);
    const [serverTime, setServerTime] = useState(null);
    const [projectId, setProjectId] = useState('1');
    const [healthData, setHealthData] = useState(null);

    // Токен берём из localStorage — тот же что и для всего API школы.
    // Если ты залогинен — там валидный JWT с {id, exp}. Если гость — токен
    // с {guest: true}, и сервер откажет с 403 'guests_not_allowed' (это нормально).
    const tokenInfo = (() => {
        try {
            const raw = localStorage.getItem('Authorization');
            if (!raw) return { hasToken: false, isGuest: false, userId: null };
            const t = raw.startsWith('Bearer ') ? raw.slice(7) : raw;
            const p = JSON.parse(atob(t.split('.')[1]));
            const isGuest = p?.guest === true || p?.type === 'guest'
                || (typeof p?.sub === 'string' && p.sub.startsWith('guest_'));
            return { hasToken: true, isGuest, userId: p?.id || null, raw };
        } catch (e) {
            return { hasToken: false, isGuest: false, userId: null };
        }
    })();

    const [moving, setMoving] = useState(false);
    /** WASD-режим — клиент сам управляет своим игроком клавишами. */
    const [wasdMode, setWasdMode] = useState(false);

    /** Модалка «Куда подключиться?». 'closed' | 'list' | 'by-code'. */
    const [joinModal, setJoinModal] = useState('closed');
    /** Список доступных публичных комнат для projectId. */
    const [availableRooms, setAvailableRooms] = useState([]);
    const [roomsLoading, setRoomsLoading] = useState(false);
    /** Введённый код приватной комнаты. */
    const [joinCode, setJoinCode] = useState('');
    /** Если мы вошли по коду — храним roomId для UI «ты в комнате XYZ». */
    const [currentRoomCode, setCurrentRoomCode] = useState('');

    const clientRef = useRef(null);
    const logRef = useRef(null);
    const moveTimerRef = useRef(null);
    const inputTimerRef = useRef(null);
    const wasdLoopRef = useRef(null);
    const canvasRef = useRef(null);
    /** Какие клавиши сейчас нажаты (управление WASD). */
    const keysRef = useRef({ w: false, a: false, s: false, d: false });
    /** Локальная позиция этого клиента — то что мы шлём серверу. */
    const myPosRef = useRef({ x: 0, y: 0, z: 0, yaw: 0 });
    /** Список игроков для рендера canvas (живой ref, не state).
     *  setPlayers триггерит ре-рендер всего React-дерева, а нам нужен
     *  только canvas — поэтому держим параллельный ref. */
    const playersRef = useRef([]);
    /** Список выстрелов-визуальных (трейсеров) для отрисовки. */
    const shotsRef = useRef([]);
    /** Список вспышек попадания для отрисовки. */
    const hitsRef = useRef([]);

    /** Добавить строку в лог. Авто-скролл к концу. */
    const log = useCallback((level, ...parts) => {
        const text = parts.map(p =>
            typeof p === 'string' ? p : JSON.stringify(p)
        ).join(' ');
        setLogs(prev => [
            ...prev,
            { level, text, ts: Date.now() },
        ].slice(-200));   // храним только последние 200 строк
    }, []);

    // Авто-скролл лога вниз
    useEffect(() => {
        const el = logRef.current;
        if (el) el.scrollTop = el.scrollHeight;
    }, [logs]);

    /**
     * Универсальное подключение. mode:
     *   'public' — joinOrCreate (попадаем в первую свободную / создаём новую публичную)
     *   'private' — create с private:true (только мы и тот кому скажем код)
     *   'by-code' — joinById по конкретному roomId
     *   'specific' — joinById по конкретной комнате из списка
     */
    const connectAs = async (mode, opts = {}) => {
        if (status === 'connected' || status === 'connecting') return;
        if (!tokenInfo.hasToken) {
            log('error', 'В localStorage нет токена. Сначала войди в аккаунт.');
            return;
        }
        if (tokenInfo.isGuest) {
            log('error', 'Гостевые токены в realtime не пускаются. Зарегистрируйся.');
            return;
        }

        setJoinModal('closed');
        setStatus('connecting');
        log('info', `Подключаемся к ${REALTIME_WS} (${mode})…`);

        try {
            const client = new Client(REALTIME_WS);
            clientRef.current = client;

            const baseOptions = {
                projectId: Number(projectId) || 0,
                token: tokenInfo.raw,
            };

            let r;
            if (mode === 'public') {
                r = await client.joinOrCreate('battle', baseOptions);
            } else if (mode === 'private') {
                r = await client.create('battle', { ...baseOptions, private: true });
            } else if (mode === 'specific') {
                // Прямой joinById с конкретным roomId из getAvailableRooms.
                if (!opts.roomId) throw new Error('roomId required');
                r = await client.joinById(opts.roomId, baseOptions);
            } else if (mode === 'by-code') {
                // Сначала ищем roomId по короткому коду через REST-endpoint
                // /find-room/:code (он ходит в matchMaker.query и матчит metadata.roomCode).
                const code = String(opts.roomId || '').toUpperCase();
                if (!code) throw new Error('code required');
                log('info', `Ищем комнату по коду ${code}…`);
                const lookupRes = await fetch(`${REALTIME_HTTP}/find-room/${encodeURIComponent(code)}`);
                if (lookupRes.status === 404) {
                    throw new Error(`Комната с кодом «${code}» не найдена`);
                }
                if (!lookupRes.ok) {
                    throw new Error(`Lookup error HTTP ${lookupRes.status}`);
                }
                const lookup = await lookupRes.json();
                log('info', `Нашли roomId=${lookup.roomId}, входим…`);
                r = await client.joinById(lookup.roomId, baseOptions);
            } else {
                throw new Error(`unknown mode: ${mode}`);
            }

            setRoom(r);
            setStatus('connected');
            const code = (r.roomId || '').slice(0, 6).toUpperCase();
            setCurrentRoomCode(code);
            log('ok', `✓ Joined room ${r.roomId} (code=${code}), sessionId=${r.sessionId}`);
            if (mode === 'private') {
                log('info', `🔑 Код приватной комнаты: ${code} — поделись им с другом`);
            }

            // === Подписка на state ===
            // В Colyseus 0.16 state-колбэки прокидываются через getStateCallbacks(room).
            // На сервере (@colyseus/schema 3.x) поля стали "lazy", и .onAdd на MapSchema
            // напрямую больше не работает — нужен фасад $.
            const $ = getStateCallbacks(r);

            $(r.state).players.onAdd((player, sessionId) => {
                log('info', `+ player ${sessionId} (${player.username})`);
                const entry = {
                    sessionId,
                    username: player.username,
                    x: player.x, y: player.y, z: player.z, yaw: player.yaw || 0,
                    hp: player.hp ?? 100,
                    maxHp: player.maxHp ?? 100,
                    isDead: !!player.isDead,
                    kills: player.kills || 0,
                    deaths: player.deaths || 0,
                };
                playersRef.current = [
                    ...playersRef.current.filter(p => p.sessionId !== sessionId),
                    entry,
                ];
                setPlayers([...playersRef.current]);

                // Подписка на изменения полей этого Player'а — координаты приходят
                // из state-snapshot'ов сервера (после нашего onMessage 'input').
                // Без этого state-callbacks мы бы не знали, что player.x изменился.
                $(player).onChange(() => {
                    const idx = playersRef.current.findIndex(p => p.sessionId === sessionId);
                    if (idx === -1) return;
                    playersRef.current[idx] = {
                        ...playersRef.current[idx],
                        x: player.x, y: player.y, z: player.z, yaw: player.yaw || 0,
                        hp: player.hp,
                        maxHp: player.maxHp,
                        isDead: !!player.isDead,
                        kills: player.kills || 0,
                        deaths: player.deaths || 0,
                    };
                    // Не вызываем setPlayers на каждый input — слишком часто
                    // (40+ раз/сек = тормозит React). Список перерисуем позже,
                    // canvas же рисует напрямую из playersRef в своём 60-FPS-loop'е.
                });
            }, true);   // immediate=true — обработать уже существующих игроков
            $(r.state).players.onRemove((player, sessionId) => {
                log('warn', `- player ${sessionId} (${player.username})`);
                playersRef.current = playersRef.current.filter(p => p.sessionId !== sessionId);
                setPlayers([...playersRef.current]);
            });

            // serverTime обновляется 20 раз/сек — пишем не каждый,
            // только раз в 2 сек, чтобы не спамить лог.
            let lastLoggedSecond = 0;
            $(r.state).listen('serverTime', (t) => {
                setServerTime(t);
                const sec = Math.floor(t / 2000);
                if (sec !== lastLoggedSecond) {
                    lastLoggedSecond = sec;
                    log('debug', `tick serverTime=${new Date(t).toISOString().slice(11, 23)}`);
                }
            });

            // === Сообщения ===
            r.onMessage('welcome', (m) => {
                log('ok', '← welcome', m);
            });

            // Выстрел кого-то (включая нас) — рисуем трейсер на 200мс
            r.onMessage('shot', (m) => {
                shotsRef.current.push({
                    originX: m.originX, originZ: m.originZ,
                    dirX: m.dirX, dirZ: m.dirZ,
                    distance: m.hitDistance || 30,
                    hit: !!m.hit,
                    expiresAt: Date.now() + 200,
                });
            });

            // Попадание — короткая красная вспышка вокруг victim'а
            r.onMessage('hit', (m) => {
                hitsRef.current.push({
                    sessionId: m.victimSessionId,
                    expiresAt: Date.now() + 250,
                });
                if (m.victimSessionId === r.sessionId) {
                    log('warn', `🩸 Получил урон ${m.damage}! HP=${m.victimHp}/${m.victimMaxHp}`);
                } else if (m.shooterSessionId === r.sessionId) {
                    log('ok', `🎯 Попал в ${m.victimSessionId}, нанёс ${m.damage} урона`);
                }
            });

            r.onMessage('kill', (m) => {
                if (m.killerSessionId === r.sessionId) {
                    log('ok', `💀 Убил ${m.victimName}!`);
                } else if (m.victimSessionId === r.sessionId) {
                    log('warn', `☠️ Тебя убил ${m.killerName}. Респаун через 2с…`);
                } else {
                    log('info', `${m.killerName} убил ${m.victimName}`);
                }
            });

            r.onMessage('respawn', (m) => {
                if (m.sessionId === r.sessionId) {
                    log('ok', '✨ Респаун! Вернулся в (0,0,0) с полным HP');
                    // Сбросим локальную позицию — иначе клиент сразу зашлёт
                    // input со старой и сервер опять увидит «телепорт».
                    myPosRef.current = { x: 0, y: 0, z: 0, yaw: 0 };
                }
            });

            r.onLeave((code) => {
                log('warn', `Disconnected (code=${code})`);
                stopMoving();
                stopWasd();
                setStatus('disconnected');
                setRoom(null);
                playersRef.current = [];
                shotsRef.current = [];
                hitsRef.current = [];
                setPlayers([]);
            });

            r.onError((code, message) => {
                log('error', `Room error: code=${code}, message=${message}`);
            });

        } catch (e) {
            setStatus('disconnected');
            // ServerError из onAuth прилетает сюда: e.code и e.message
            // (например, 403 + 'Мультиплеер доступен только зарегистрированным…').
            const code = e?.code ?? '';
            const msg = e?.message || String(e);
            if (code) {
                log('error', `Сервер отказал (code=${code}): ${msg}`);
            } else {
                log('error', 'Не удалось подключиться:', msg);
            }
            console.error('[RealtimeTest] connect error:', e);
        }
    };

    /** Получить список ПУБЛИЧНЫХ комнат для текущего projectId через
     *  собственный realtime-endpoint /list-rooms/:projectId.
     *  Стандартный /matchmake/battle в Colyseus 0.16 не работает как listing
     *  (он только для joinOrCreate/create/join/joinById/reconnect). */
    const fetchAvailableRooms = useCallback(async () => {
        setRoomsLoading(true);
        try {
            const pid = encodeURIComponent(String(Number(projectId) || 0));
            const res = await fetch(`${REALTIME_HTTP}/list-rooms/${pid}`);
            if (!res.ok) throw new Error(`HTTP ${res.status}`);
            const rooms = await res.json();   // массив room-info
            setAvailableRooms(Array.isArray(rooms) ? rooms : []);
        } catch (err) {
            log('error', 'Не удалось получить список комнат:', err?.message || err);
            setAvailableRooms([]);
        } finally {
            setRoomsLoading(false);
        }
    }, [projectId, log]);

    /** Послать текущую позицию серверу. */
    const sendInput = useCallback(() => {
        if (!room) return;
        const p = myPosRef.current;
        room.send('input', { x: p.x, y: p.y, z: p.z, yaw: p.yaw });
    }, [room]);

    /** Выстрел в направлении (worldX, worldZ) от текущей позиции игрока. */
    const shootAt = useCallback((worldX, worldZ) => {
        if (!room) return;
        const p = myPosRef.current;
        const dx = worldX - p.x;
        const dz = worldZ - p.z;
        const len = Math.hypot(dx, dz);
        if (len < 0.001) return;
        room.send('shoot', {
            originX: p.x,
            originZ: p.z,
            dirX: dx / len,
            dirZ: dz / len,
        });
    }, [room]);

    /** Включить WASD-режим. Слушаем клавиши, в локальном loop'е считаем
     *  позицию, шлём её серверу 20 раз/сек. */
    const startWasd = useCallback(() => {
        if (!room || wasdLoopRef.current) return;
        log('info', '⌨️ WASD-режим включён (5 м/с). Кликни на canvas.');
        setWasdMode(true);

        const SPEED = 5;   // метров в секунду
        let lastTs = Date.now();

        wasdLoopRef.current = setInterval(() => {
            const now = Date.now();
            const dt = (now - lastTs) / 1000;   // секунды
            lastTs = now;

            const k = keysRef.current;
            let vx = 0, vz = 0;
            if (k.w) vz -= 1;
            if (k.s) vz += 1;
            if (k.a) vx -= 1;
            if (k.d) vx += 1;
            // Нормализуем диагональ — без этого вход (1,1) даёт скорость √2
            const len = Math.hypot(vx, vz);
            if (len > 0) { vx /= len; vz /= len; }

            const p = myPosRef.current;
            myPosRef.current = {
                x: p.x + vx * SPEED * dt,
                y: 0,
                z: p.z + vz * SPEED * dt,
                yaw: (vx === 0 && vz === 0) ? p.yaw : Math.atan2(vx, -vz),
            };
        }, 16);

        // Шлём серверу 20 раз/сек
        if (!inputTimerRef.current) {
            inputTimerRef.current = setInterval(sendInput, 50);
        }
    }, [room, sendInput, log]);

    const stopWasd = useCallback(() => {
        if (wasdLoopRef.current) {
            clearInterval(wasdLoopRef.current);
            wasdLoopRef.current = null;
        }
        if (inputTimerRef.current && !moveTimerRef.current) {
            clearInterval(inputTimerRef.current);
            inputTimerRef.current = null;
        }
        keysRef.current = { w: false, a: false, s: false, d: false };
        setWasdMode(false);
        log('info', '⌨️ WASD-режим выключен');
    }, [log]);

    // Слушаем WASD на window. Активны только в WASD-режиме.
    useEffect(() => {
        if (!wasdMode) return;
        const map = { KeyW: 'w', KeyA: 'a', KeyS: 's', KeyD: 'd' };
        const onDown = (e) => {
            const k = map[e.code];
            if (k) { keysRef.current[k] = true; e.preventDefault(); }
        };
        const onUp = (e) => {
            const k = map[e.code];
            if (k) { keysRef.current[k] = false; }
        };
        window.addEventListener('keydown', onDown);
        window.addEventListener('keyup', onUp);
        return () => {
            window.removeEventListener('keydown', onDown);
            window.removeEventListener('keyup', onUp);
        };
    }, [wasdMode]);

    /** Симулировать движение по кругу: меняем myPos.x/z по синусу/косинусу.
     *  Радиус наращивается плавно за первые 2 секунды, чтобы не было
     *  телепорта в (5,0,0) и сервер не отбил sanity-check'ом. */
    const startMoving = useCallback(() => {
        if (!room) return;
        if (moveTimerRef.current) return;

        log('info', '🎮 Симуляция движения запущена (круг радиусом 5м, плавный старт)');
        setMoving(true);

        const startTs = Date.now();
        const TARGET_R = 5;       // целевой радиус
        const RAMP_SEC = 2;       // время наращивания радиуса
        const omega = (Math.PI * 2) / 6;   // период 6 сек

        // 60 FPS симуляция позиции — обновляем myPosRef
        moveTimerRef.current = setInterval(() => {
            const t = (Date.now() - startTs) / 1000;
            // Текущий радиус: от 0 до TARGET_R за RAMP_SEC, потом константа.
            const r = t < RAMP_SEC ? TARGET_R * (t / RAMP_SEC) : TARGET_R;
            myPosRef.current = {
                x: r * Math.cos(omega * t),
                y: 0,
                z: r * Math.sin(omega * t),
                // yaw — направление движения (касательная к кругу)
                yaw: Math.atan2(Math.cos(omega * t), -Math.sin(omega * t)),
            };
        }, 16);

        // Шлём input серверу 20 раз/сек (внутри лимита 30/сек)
        inputTimerRef.current = setInterval(sendInput, 50);
    }, [room, sendInput, log]);

    const stopMoving = useCallback(() => {
        if (moveTimerRef.current) {
            clearInterval(moveTimerRef.current);
            moveTimerRef.current = null;
        }
        if (inputTimerRef.current) {
            clearInterval(inputTimerRef.current);
            inputTimerRef.current = null;
        }
        setMoving(false);
        log('info', '⏸ Симуляция движения остановлена');
    }, [log]);

    /** Уйти из комнаты. */
    const disconnect = async () => {
        stopMoving();
        stopWasd();
        if (room) {
            try { await room.leave(true); } catch (e) {}
            setRoom(null);
        }
        setStatus('disconnected');
        setPlayers([]);
        setCurrentRoomCode('');
        log('info', 'Disconnected manually');
    };

    /** Отправить тестовое 'hello'. */
    const sendHello = () => {
        if (!room) return;
        const payload = { foo: 'bar', t: Date.now() };
        room.send('hello', payload);
        log('info', '→ hello', payload);
    };

    /** Проверить /health. */
    const checkHealth = async () => {
        log('info', `GET ${REALTIME_HTTP}/health`);
        try {
            const res = await fetch(`${REALTIME_HTTP}/health`);
            const data = await res.json();
            setHealthData(data);
            log('ok', '/health →', data);
        } catch (e) {
            setHealthData({ ok: false, error: e?.message });
            log('error', '/health failed:', e?.message);
        }
    };

    // Когда открыли модалку «Список комнат» — сразу подгружаем
    // и обновляем каждые 3 секунды, пока модалка открыта.
    useEffect(() => {
        if (joinModal !== 'list') return;
        fetchAvailableRooms();
        const t = setInterval(fetchAvailableRooms, 3000);
        return () => clearInterval(t);
    }, [joinModal, fetchAvailableRooms]);

    // Раз в 250мс синхронизируем state players ← playersRef. Это нужно потому что
    // мы НЕ вызываем setPlayers в onChange (40+ раз/сек = тормозит React),
    // и без этого таймера правый список «Игроки в комнате» был бы статичным.
    useEffect(() => {
        if (status !== 'connected') return;
        const t = setInterval(() => {
            setPlayers([...playersRef.current]);
        }, 250);
        return () => clearInterval(t);
    }, [status]);

    // === Canvas: top-down 2D-сцена ===
    // Рисуем 60 раз/сек: пол-сетка, всех игроков, ник над аватаркой.
    // Координаты: 1 м = 8 px, центр canvas = (0, 0) в мире.
    useEffect(() => {
        if (status !== 'connected') return;
        const canvas = canvasRef.current;
        if (!canvas) return;
        const ctx = canvas.getContext('2d');
        const W = canvas.width;
        const H = canvas.height;
        const SCALE = 8;       // пикселей на метр
        const cx = W / 2;
        const cy = H / 2;

        // Клик по canvas = выстрел в это место мира.
        // Работает только в WASD-режиме, чтобы случайно не настрелять при простой инспекции.
        const onClick = (e) => {
            if (!wasdMode) return;
            const rect = canvas.getBoundingClientRect();
            const px = e.clientX - rect.left;
            const py = e.clientY - rect.top;
            // px/py → world coordinates
            const worldX = (px - cx) / SCALE;
            const worldZ = (py - cy) / SCALE;
            shootAt(worldX, worldZ);
        };
        canvas.addEventListener('click', onClick);

        let raf = 0;
        const draw = () => {
            // Фон
            ctx.fillStyle = '#0a0e1a';
            ctx.fillRect(0, 0, W, H);

            // Сетка (1 м)
            ctx.strokeStyle = 'rgba(51, 87, 255, 0.08)';
            ctx.lineWidth = 1;
            for (let x = -30; x <= 30; x++) {
                const sx = cx + x * SCALE;
                ctx.beginPath();
                ctx.moveTo(sx, 0);
                ctx.lineTo(sx, H);
                ctx.stroke();
            }
            for (let z = -30; z <= 30; z++) {
                const sy = cy + z * SCALE;
                ctx.beginPath();
                ctx.moveTo(0, sy);
                ctx.lineTo(W, sy);
                ctx.stroke();
            }
            // Оси (через центр)
            ctx.strokeStyle = 'rgba(51, 87, 255, 0.30)';
            ctx.lineWidth = 1.5;
            ctx.beginPath();
            ctx.moveTo(cx, 0); ctx.lineTo(cx, H);
            ctx.moveTo(0, cy); ctx.lineTo(W, cy);
            ctx.stroke();

            // Чистим устаревшие выстрелы и попадания
            const now = Date.now();
            shotsRef.current = shotsRef.current.filter(s => s.expiresAt > now);
            hitsRef.current = hitsRef.current.filter(h => h.expiresAt > now);

            // Трейсеры выстрелов (под игроками)
            for (const s of shotsRef.current) {
                const t = (s.expiresAt - now) / 200;   // 1 → 0
                const sx = cx + s.originX * SCALE;
                const sy = cy + s.originZ * SCALE;
                const ex = cx + (s.originX + s.dirX * s.distance) * SCALE;
                const ez = cy + (s.originZ + s.dirZ * s.distance) * SCALE;
                ctx.strokeStyle = s.hit
                    ? `rgba(255, 100, 100, ${t})`
                    : `rgba(255, 230, 100, ${t * 0.7})`;
                ctx.lineWidth = 2;
                ctx.beginPath();
                ctx.moveTo(sx, sy);
                ctx.lineTo(ex, ez);
                ctx.stroke();
            }

            // Игроки
            const myId = room?.sessionId;
            for (const p of playersRef.current) {
                const sx = cx + p.x * SCALE;
                const sy = cy + p.z * SCALE;
                const isMe = p.sessionId === myId;
                const isDead = !!p.isDead;

                // Вспышка попадания (красное кольцо вокруг)
                const hit = hitsRef.current.find(h => h.sessionId === p.sessionId);
                if (hit) {
                    const t = (hit.expiresAt - now) / 250;
                    ctx.strokeStyle = `rgba(255, 60, 60, ${t})`;
                    ctx.lineWidth = 4;
                    ctx.beginPath();
                    ctx.arc(sx, sy, 14 + (1 - t) * 6, 0, Math.PI * 2);
                    ctx.stroke();
                }

                // Кружок-игрок
                ctx.fillStyle = isDead
                    ? '#475569'
                    : (isMe ? '#3357ff' : '#94a3b8');
                ctx.globalAlpha = isDead ? 0.45 : 1;
                ctx.beginPath();
                ctx.arc(sx, sy, 10, 0, Math.PI * 2);
                ctx.fill();
                ctx.globalAlpha = 1;

                if (!isDead) {
                    // Стрелка-yaw
                    ctx.strokeStyle = '#fff';
                    ctx.lineWidth = 2;
                    ctx.beginPath();
                    ctx.moveTo(sx, sy);
                    ctx.lineTo(
                        sx + Math.sin(p.yaw) * 14,
                        sy - Math.cos(p.yaw) * 14
                    );
                    ctx.stroke();
                }

                // HP-бар над игроком
                if (!isDead) {
                    const hpPct = (p.hp || 0) / (p.maxHp || 100);
                    const barW = 28;
                    const barH = 4;
                    const barY = sy - 22;
                    // Фон
                    ctx.fillStyle = 'rgba(0,0,0,0.5)';
                    ctx.fillRect(sx - barW / 2 - 1, barY - 1, barW + 2, barH + 2);
                    // Заливка
                    ctx.fillStyle = hpPct > 0.5 ? '#22d97a'
                        : hpPct > 0.25 ? '#f59e0b'
                        : '#ef4444';
                    ctx.fillRect(sx - barW / 2, barY, barW * hpPct, barH);
                }

                // Ник
                ctx.font = 'bold 11px "Roboto Condensed", sans-serif';
                ctx.textAlign = 'center';
                ctx.textBaseline = 'bottom';
                const label = isDead ? `💀 ${p.username}` : p.username;
                const textW = ctx.measureText(label).width;
                ctx.fillStyle = isDead
                    ? 'rgba(70, 80, 100, 0.85)'
                    : (isMe ? 'rgba(51, 87, 255, 0.85)' : 'rgba(20, 24, 45, 0.85)');
                ctx.fillRect(sx - textW / 2 - 4, sy - 38, textW + 8, 14);
                ctx.fillStyle = '#fff';
                ctx.fillText(label, sx, sy - 26);
            }
            raf = requestAnimationFrame(draw);
        };
        raf = requestAnimationFrame(draw);
        return () => {
            cancelAnimationFrame(raf);
            canvas.removeEventListener('click', onClick);
        };
    }, [status, room, wasdMode, shootAt]);

    // При размонтировании — гарантированно отключаемся и стопаем таймеры
    useEffect(() => () => {
        if (moveTimerRef.current) clearInterval(moveTimerRef.current);
        if (inputTimerRef.current) clearInterval(inputTimerRef.current);
        if (wasdLoopRef.current) clearInterval(wasdLoopRef.current);
        if (room) {
            try { room.leave(true); } catch (e) {}
        }
    }, [room]);

    return (
        <div style={{
            minHeight: '100vh',
            background: KT.bg,
            color: KT.text,
            fontFamily: KT.font,
        }}>
            <style>{KUBIKON_KEYFRAMES}</style>

            {/* Header */}
            <div style={{
                background: KT.glassDark,
                backdropFilter: 'blur(20px)',
                borderBottom: `1px solid ${KT.borderSoft}`,
                padding: '14px 24px',
                position: 'sticky', top: 0, zIndex: 50,
            }}>
                <div style={{
                    maxWidth: 1240, margin: '0 auto',
                    display: 'flex', alignItems: 'center', gap: 16,
                }}>
                    <Link to="/" style={{
                        color: KT.text, textDecoration: 'none',
                        fontSize: 14, fontWeight: 700,
                        padding: '8px 14px',
                        background: KT.bgPage,
                        border: `1px solid ${KT.border}`,
                        borderRadius: 999,
                        boxShadow: KT.shadowSm,
                    }}><Icon name="arrow-left" size={13} /> В Studio</Link>

                    <div style={{
                        display: 'inline-flex', alignItems: 'center', gap: 10,
                    }}>
                        <RublocsLogo size={32} />
                        <span style={{
                            fontSize: 22, fontWeight: 900, color: KT.text,
                            letterSpacing: -0.5,
                        }}>Realtime Test</span>
                        <StatusPill status={status} />
                    </div>

                    <div style={{ marginLeft: 'auto', fontSize: 12, color: KT.textMuted }}>
                        Подэтап 4.1 — Hello-room
                    </div>
                </div>
            </div>

            <div style={{ maxWidth: 1240, margin: '0 auto', padding: '32px 24px 64px' }}>
                {/* Контролы */}
                <div style={{
                    background: KT.bgPage,
                    border: `1px solid ${KT.border}`,
                    borderRadius: KT.radiusXl,
                    padding: 24,
                    marginBottom: 20,
                    boxShadow: KT.shadowMd,
                }}>
                    <h2 style={sectionTitleStyle}><Icon name="settings" size={13} /> Параметры подключения</h2>
                    <div style={{
                        display: 'grid',
                        gridTemplateColumns: '1fr 1fr',
                        gap: 16,
                        marginBottom: 18,
                    }}>
                        <Field
                            label="Project ID"
                            value={projectId}
                            onChange={setProjectId}
                            placeholder="1"
                            disabled={status !== 'disconnected'}
                        />
                        <TokenInfo info={tokenInfo} />
                    </div>
                    <div style={{ display: 'flex', gap: 10, flexWrap: 'wrap' }}>
                        <Btn
                            primary
                            disabled={status !== 'disconnected'
                                || !tokenInfo.hasToken
                                || tokenInfo.isGuest}
                            onClick={() => setJoinModal('list')}
                        >
                            <Icon name="link" size={13} /> Подключиться…
                        </Btn>
                        <Btn
                            disabled={status !== 'connected'}
                            onClick={disconnect}
                        >
                            <Icon name="stop" size={13} /> Отключиться
                        </Btn>
                        <Btn
                            disabled={status !== 'connected'}
                            onClick={sendHello}
                        >
                            <Icon name="send" size={13} /> Отправить hello
                        </Btn>
                        {!moving ? (
                            <Btn
                                disabled={status !== 'connected' || wasdMode}
                                onClick={startMoving}
                            >
                                <Icon name="cycle" size={13} /> По кругу
                            </Btn>
                        ) : (
                            <Btn
                                disabled={status !== 'connected'}
                                onClick={stopMoving}
                            >
                                <Icon name="pause" size={13} /> Стоп круг
                            </Btn>
                        )}
                        {!wasdMode ? (
                            <Btn
                                disabled={status !== 'connected' || moving}
                                onClick={startWasd}
                            >
                                <Icon name="keyboard" size={13} /> WASD-режим
                            </Btn>
                        ) : (
                            <Btn
                                disabled={status !== 'connected'}
                                onClick={stopWasd}
                            >
                                <Icon name="pause" size={13} /> Стоп WASD
                            </Btn>
                        )}
                        <Btn onClick={checkHealth}><Icon name="stethoscope" size={13} /> GET /health</Btn>
                        <Btn onClick={() => setLogs([])}><Icon name="delete" size={13} /> Очистить лог</Btn>
                    </div>
                    <div style={{
                        marginTop: 14, fontSize: 12, color: KT.textMuted,
                        fontFamily: 'monospace',
                    }}>
                        WS: {REALTIME_WS} · HTTP: {REALTIME_HTTP}
                        {currentRoomCode && status === 'connected' && (
                            <span style={{
                                marginLeft: 16,
                                background: KT.accent, color: '#fff',
                                padding: '3px 10px', borderRadius: 999,
                                fontWeight: 800, letterSpacing: 1,
                            }}>
                                <Icon name="tag" size={12} /> {currentRoomCode}
                            </span>
                        )}
                    </div>
                </div>

                {/* === 2D-сцена === */}
                <div style={{ ...cardStyle, marginBottom: 20 }}>
                    <h3 style={sectionTitleStyle}>
                        <Icon name="map" size={15} /> Сцена (top-down)
                        <span style={{
                            fontSize: 11, fontWeight: 600, color: KT.textMuted,
                            marginLeft: 12, letterSpacing: 0,
                            textTransform: 'none',
                        }}>
                            1 м = 8 px · ось X →, ось Z ↓ · кружок = игрок, стрелка = yaw
                        </span>
                    </h3>
                    <div style={{
                        display: 'flex', justifyContent: 'center',
                        background: '#0a0e1a',
                        borderRadius: KT.radius,
                        border: `1px solid ${KT.border}`,
                        padding: 8,
                    }}>
                        <canvas
                            ref={canvasRef}
                            width={600}
                            height={400}
                            style={{
                                display: 'block',
                                borderRadius: 8,
                                outline: wasdMode ? `2px solid ${KT.accent}` : 'none',
                                cursor: wasdMode ? 'crosshair' : 'default',
                            }}
                        />
                    </div>
                    {wasdMode && (
                        <div style={{
                            marginTop: 12,
                            padding: '10px 14px',
                            background: KT.accentSoft,
                            border: `1px solid ${KT.accent}`,
                            borderRadius: KT.radius,
                            fontSize: 13, fontWeight: 600,
                            color: KT.accentDeep,
                        }}><Icon name="keyboard" size={14} /><b>WASD</b> — двигаешься (5 м/с). 🔫 <b>Клик по сцене</b> —
                            выстрел в ту точку (25 урона, дальность 30м, до 5 выстрелов/сек).
                            При HP=0 — респаун через 2 секунды.
                        </div>
                    )}
                </div>

                {/* Health + game state */}
                <div style={{
                    display: 'grid',
                    gridTemplateColumns: '1fr 1fr',
                    gap: 20,
                    marginBottom: 20,
                }}>
                    {/* Health */}
                    <div style={cardStyle}>
                        <h3 style={sectionTitleStyle}><Icon name="stethoscope" size={13} /> Health</h3>
                        {healthData ? (
                            <pre style={preStyle}>
                                {JSON.stringify(healthData, null, 2)}
                            </pre>
                        ) : (
                            <div style={{ color: KT.textMuted, fontSize: 13 }}>
                                Нажми «GET /health» — проверим, что HTTP-сервер жив.
                            </div>
                        )}
                    </div>

                    {/* Players */}
                    <div style={cardStyle}>
                        <h3 style={sectionTitleStyle}>
                            <Icon name="users" size={15} /> Игроки в комнате
                            <span style={pillStyle}>{players.length}</span>
                        </h3>
                        {players.length === 0 ? (
                            <div style={{ color: KT.textMuted, fontSize: 13 }}>
                                Подключись и зайди в комнату — здесь появишься ты,
                                а потом и другие игроки (если откроешь страницу
                                в другой вкладке).
                            </div>
                        ) : (
                            <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
                                {players.map(p => {
                                    const isMe = p.sessionId === room?.sessionId;
                                    return (
                                        <div key={p.sessionId} style={{
                                            background: isMe ? KT.accentSoft : KT.bgSubtle,
                                            border: `1px solid ${isMe ? KT.accent : KT.borderSoft}`,
                                            borderRadius: KT.radius,
                                            padding: '10px 14px',
                                            display: 'flex', alignItems: 'center', gap: 12,
                                        }}>
                                            <div style={{
                                                width: 32, height: 32, borderRadius: '50%',
                                                background: KT.gradientBrand,
                                                color: '#fff', display: 'flex',
                                                alignItems: 'center', justifyContent: 'center',
                                                fontSize: 14, fontWeight: 800,
                                            }}>
                                                {p.username.slice(0, 1).toUpperCase()}
                                            </div>
                                            <div style={{ flex: 1, minWidth: 0 }}>
                                                <div style={{
                                                    fontSize: 14, fontWeight: 800, color: KT.text,
                                                    display: 'flex', alignItems: 'center', gap: 6,
                                                    flexWrap: 'wrap',
                                                }}>
                                                    {p.isDead && <span><Icon name="skull" size={14} /></span>}
                                                    {p.username}
                                                    {isMe && (
                                                        <span style={{
                                                            fontSize: 10,
                                                            fontWeight: 800, color: KT.accent,
                                                            textTransform: 'uppercase', letterSpacing: 0.6,
                                                        }}>(ты)</span>
                                                    )}
                                                    <span style={{
                                                        fontSize: 11, fontWeight: 800,
                                                        color: KT.success,
                                                        background: KT.successLight || '#ecfdf5',
                                                        padding: '1px 6px', borderRadius: 999,
                                                    }}>K {p.kills || 0}</span>
                                                    <span style={{
                                                        fontSize: 11, fontWeight: 800,
                                                        color: KT.danger,
                                                        background: '#fef2f2',
                                                        padding: '1px 6px', borderRadius: 999,
                                                    }}>D {p.deaths || 0}</span>
                                                </div>
                                                {/* HP-бар */}
                                                <div style={{
                                                    marginTop: 4, height: 6,
                                                    background: '#e5e7eb',
                                                    borderRadius: 999,
                                                    overflow: 'hidden',
                                                }}>
                                                    <div style={{
                                                        height: '100%',
                                                        width: `${((p.hp || 0) / (p.maxHp || 100)) * 100}%`,
                                                        background: (p.hp || 0) > 50 ? '#22d97a'
                                                            : (p.hp || 0) > 25 ? '#f59e0b'
                                                            : '#ef4444',
                                                        transition: 'width 200ms ease, background 200ms ease',
                                                    }} />
                                                </div>
                                                <div style={{
                                                    fontSize: 11, color: KT.textMuted,
                                                    fontFamily: 'monospace',
                                                    fontVariantNumeric: 'tabular-nums',
                                                    marginTop: 3,
                                                }}>
                                                    HP {Math.round(p.hp || 0)}/{p.maxHp || 100} · ({p.x.toFixed(1)}, {p.z.toFixed(1)})
                                                </div>
                                            </div>
                                        </div>
                                    );
                                })}
                            </div>
                        )}
                        {serverTime && (
                            <div style={{
                                marginTop: 12, fontSize: 11, color: KT.textMuted,
                                fontFamily: 'monospace',
                            }}>
                                Server time: {new Date(serverTime).toISOString()}
                            </div>
                        )}
                    </div>
                </div>

                {/* Лог */}
                <div style={cardStyle}>
                    <h3 style={sectionTitleStyle}><Icon name="duplicate" size={13} /> Лог ({logs.length})</h3>
                    <div ref={logRef} style={{
                        background: '#0f1326',
                        border: `1px solid ${KT.border}`,
                        borderRadius: KT.radius,
                        padding: 14,
                        maxHeight: 360,
                        overflowY: 'auto',
                        fontFamily: 'Consolas, Menlo, monospace',
                        fontSize: 12.5,
                        lineHeight: 1.5,
                    }}>
                        {logs.length === 0 ? (
                            <div style={{ color: '#94a3b8', fontStyle: 'italic' }}>
                                События появятся здесь после подключения.
                            </div>
                        ) : logs.map((l, i) => (
                            <div key={i} style={{
                                color: LEVEL_COLORS[l.level] || '#f1f5fb',
                                whiteSpace: 'pre-wrap', wordBreak: 'break-word',
                            }}>
                                <span style={{ color: '#64748b', marginRight: 10 }}>
                                    {new Date(l.ts).toISOString().slice(11, 23)}
                                </span>
                                {l.text}
                            </div>
                        ))}
                    </div>
                </div>
            </div>

            {/* === Модалка «Куда подключиться?» === */}
            {joinModal !== 'closed' && (
                <JoinModal
                    mode={joinModal}
                    setMode={setJoinModal}
                    rooms={availableRooms}
                    roomsLoading={roomsLoading}
                    onRefresh={fetchAvailableRooms}
                    joinCode={joinCode}
                    setJoinCode={setJoinCode}
                    onConnect={connectAs}
                />
            )}
        </div>
    );
};

// =============================================================
// === Модалка «Куда подключиться?» ===
// =============================================================
const JoinModal = ({ mode, setMode, rooms, roomsLoading, onRefresh,
                    joinCode, setJoinCode, onConnect }) => {
    return (
        <div
            onClick={() => setMode('closed')}
            style={{
                position: 'fixed', inset: 0, zIndex: 100,
                background: 'rgba(15, 23, 42, 0.55)',
                backdropFilter: 'blur(8px)',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                padding: 20, animation: 'kubikonFadeIn 200ms ease',
            }}
        >
            <div
                onClick={(e) => e.stopPropagation()}
                style={{
                    background: '#fff',
                    borderRadius: 22,
                    width: '100%', maxWidth: 540,
                    color: KT.text,
                    fontFamily: KT.font,
                    boxShadow: KT.shadowLg,
                    overflow: 'hidden',
                    animation: 'kubikonFadeInScale 280ms cubic-bezier(0.34, 1.56, 0.64, 1)',
                }}
            >
                {/* Header с табами */}
                <div style={{
                    background: KT.gradientBrand,
                    padding: '20px 24px',
                    color: '#fff',
                    display: 'flex', alignItems: 'center', gap: 12,
                }}>
                    <div style={{
                        width: 40, height: 40, borderRadius: 12,
                        background: 'rgba(255,255,255,0.18)',
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        fontSize: 20,
                    }}><Icon name="globe" size={14} /></div>
                    <div style={{ flex: 1 }}>
                        <div style={{ fontSize: 18, fontWeight: 800, letterSpacing: -0.3 }}>
                            Подключиться к мультиплееру
                        </div>
                        <div style={{ fontSize: 12, color: 'rgba(255,255,255,0.78)', marginTop: 2 }}>
                            Выбери комнату или создай новую
                        </div>
                    </div>
                    <button
                        onClick={() => setMode('closed')}
                        style={{
                            width: 32, height: 32,
                            background: 'rgba(255,255,255,0.16)',
                            border: '1px solid rgba(255,255,255,0.25)',
                            color: '#fff', borderRadius: 10,
                            fontSize: 20, cursor: 'pointer',
                            fontFamily: 'inherit',
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                        }}
                    >×</button>
                </div>

                {/* Tabs */}
                <div style={{
                    display: 'flex', gap: 0,
                    borderBottom: `1px solid ${KT.borderSoft}`,
                    background: KT.bgSubtle,
                }}>
                    <ModalTab active={mode === 'list'} onClick={() => setMode('list')}>
                        <Icon name="globe" size={13} /> Публичные
                    </ModalTab>
                    <ModalTab active={mode === 'by-code'} onClick={() => setMode('by-code')}>
                        <Icon name="tag" size={13} /> По коду
                    </ModalTab>
                </div>

                <div style={{ padding: 24 }}>
                    {mode === 'list' && (
                        <>
                            <div style={{
                                display: 'flex', alignItems: 'center',
                                marginBottom: 12,
                            }}>
                                <span style={{ fontSize: 13, color: KT.textSecondary, fontWeight: 600 }}>
                                    Активные комнаты этой игры:
                                </span>
                                <button
                                    onClick={onRefresh}
                                    disabled={roomsLoading}
                                    style={{
                                        marginLeft: 'auto',
                                        background: KT.bgPage,
                                        border: `1px solid ${KT.border}`,
                                        borderRadius: 999,
                                        padding: '4px 12px',
                                        fontSize: 12, fontWeight: 700,
                                        color: KT.text, cursor: 'pointer',
                                        fontFamily: 'inherit',
                                    }}
                                >
                                    {roomsLoading ? <><Icon name="hourglass" size={13} /> загрузка</> : <><Icon name="refresh" size={13} /> обновить</>}
                                </button>
                            </div>

                            <div style={{
                                maxHeight: 240, overflowY: 'auto',
                                display: 'flex', flexDirection: 'column', gap: 6,
                                marginBottom: 16,
                            }}>
                                {rooms.length === 0 && !roomsLoading && (
                                    <div style={{
                                        padding: '24px 16px', textAlign: 'center',
                                        background: KT.bgSubtle,
                                        border: `1px dashed ${KT.borderStrong}`,
                                        borderRadius: 12,
                                        color: KT.textSecondary, fontSize: 13,
                                    }}><Icon name="sprout" size={13} /> Активных публичных комнат нет.<br />
                                        Создай новую — нажми «Создать публичную».
                                    </div>
                                )}
                                {rooms.map(r => (
                                    <button
                                        key={r.roomId}
                                        onClick={() => onConnect('specific', { roomId: r.roomId })}
                                        style={{
                                            display: 'flex', alignItems: 'center', gap: 12,
                                            padding: '10px 14px',
                                            background: KT.bgSubtle,
                                            border: `1px solid ${KT.border}`,
                                            borderRadius: 12,
                                            cursor: 'pointer', fontFamily: 'inherit',
                                            textAlign: 'left',
                                            transition: 'all 150ms ease',
                                        }}
                                        onMouseEnter={(e) => {
                                            e.currentTarget.style.borderColor = KT.accent;
                                            e.currentTarget.style.background = KT.accentSoft;
                                        }}
                                        onMouseLeave={(e) => {
                                            e.currentTarget.style.borderColor = KT.border;
                                            e.currentTarget.style.background = KT.bgSubtle;
                                        }}
                                    >
                                        <div style={{
                                            width: 36, height: 36, borderRadius: 10,
                                            background: KT.gradientBrand,
                                            color: '#fff',
                                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                                            fontSize: 12, fontWeight: 800,
                                            fontFamily: 'monospace',
                                        }}>
                                            {(r.metadata?.roomCode || r.roomId.slice(0, 6)).toUpperCase()}
                                        </div>
                                        <div style={{ flex: 1, minWidth: 0 }}>
                                            <div style={{ fontSize: 14, fontWeight: 800, color: KT.text }}>
                                                {r.metadata?.roomName || `Комната #${r.roomId.slice(0, 6)}`}
                                            </div>
                                            <div style={{ fontSize: 11, color: KT.textMuted, fontWeight: 600 }}>
                                                Хост: {r.metadata?.hostUsername || '—'}
                                            </div>
                                        </div>
                                        <div style={{
                                            fontSize: 13, fontWeight: 800,
                                            color: r.clients >= r.maxClients ? KT.danger : KT.accent,
                                            fontVariantNumeric: 'tabular-nums',
                                        }}>
                                            {r.clients}/{r.maxClients}
                                        </div>
                                    </button>
                                ))}
                            </div>

                            <div style={{
                                display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10,
                            }}>
                                <button
                                    onClick={() => onConnect('public')}
                                    style={btnPrimaryModal}
                                >
                                    <Icon name="globe" size={14} /> Создать публичную
                                </button>
                                <button
                                    onClick={() => onConnect('private')}
                                    style={btnSecondaryModal}
                                >
                                    <Icon name="tag" size={14} /> Создать приватную
                                </button>
                            </div>
                            <div style={{
                                fontSize: 11, color: KT.textMuted, marginTop: 12,
                                textAlign: 'center', fontWeight: 600,
                            }}>
                                Публичная видна всем игрокам этой игры.
                                Приватная — только по коду, который ты получишь.
                            </div>
                        </>
                    )}

                    {mode === 'by-code' && (
                        <>
                            <div style={{
                                fontSize: 13, color: KT.textSecondary, fontWeight: 600,
                                marginBottom: 10,
                            }}>
                                Введи 6-символьный код, который тебе дал друг:
                            </div>
                            <input
                                type="text"
                                value={joinCode}
                                onChange={(e) => setJoinCode(
                                    e.target.value.toUpperCase().replace(/[^A-Z0-9_-]/g, '').slice(0, 12)
                                )}
                                placeholder="ABC123"
                                style={{
                                    width: '100%', boxSizing: 'border-box',
                                    background: KT.bgSubtle,
                                    border: `2px solid ${KT.border}`,
                                    borderRadius: 12,
                                    padding: '14px 18px',
                                    fontSize: 22, fontWeight: 900,
                                    fontFamily: 'monospace',
                                    color: KT.text,
                                    textAlign: 'center',
                                    letterSpacing: 4,
                                    outline: 'none',
                                }}
                                onFocus={(e) => { e.target.style.borderColor = KT.accent; }}
                                onBlur={(e) => { e.target.style.borderColor = KT.border; }}
                            />
                            <div style={{
                                fontSize: 11, color: KT.textMuted, marginTop: 8,
                                fontWeight: 500,
                            }}>
                                Код выглядит как <code style={{
                                    background: KT.bgMuted, padding: '1px 6px',
                                    borderRadius: 4, fontFamily: 'monospace', fontWeight: 700,
                                }}>ABC123</code> — друг увидит его рядом с WS-адресом
                                после подключения.
                            </div>
                            <button
                                onClick={() => onConnect('by-code', { roomId: joinCode })}
                                disabled={joinCode.length < 4}
                                style={{
                                    ...btnPrimaryModal,
                                    width: '100%', marginTop: 18,
                                    opacity: joinCode.length < 4 ? 0.5 : 1,
                                    cursor: joinCode.length < 4 ? 'not-allowed' : 'pointer',
                                }}
                            >
                                <Icon name="rocket" size={14} /> Войти в комнату
                            </button>
                        </>
                    )}
                </div>
            </div>
        </div>
    );
};

const ModalTab = ({ active, onClick, children }) => (
    <button
        onClick={onClick}
        style={{
            flex: 1, padding: '12px 16px',
            background: active ? '#fff' : 'transparent',
            border: 'none',
            borderBottom: `2px solid ${active ? KT.accent : 'transparent'}`,
            color: active ? KT.accent : KT.textSecondary,
            fontSize: 13, fontWeight: 800,
            cursor: 'pointer', fontFamily: 'inherit',
            transition: 'all 150ms ease',
        }}
    >
        {children}
    </button>
);

const btnPrimaryModal = {
    padding: '11px 20px',
    background: 'linear-gradient(135deg, #3357ff 0%, #1e2da5 100%)',
    color: '#fff',
    border: '1px solid transparent',
    borderRadius: 10,
    fontSize: 13, fontWeight: 800,
    cursor: 'pointer',
    fontFamily: '"Roboto Condensed", system-ui, sans-serif',
    boxShadow: '0 6px 16px rgba(51, 87, 255, 0.32)',
};

const btnSecondaryModal = {
    padding: '11px 20px',
    background: '#fafbfd',
    color: '#0f172a',
    border: '1.5px solid #e5e7eb',
    borderRadius: 10,
    fontSize: 13, fontWeight: 800,
    cursor: 'pointer',
    fontFamily: '"Roboto Condensed", system-ui, sans-serif',
};

const LEVEL_COLORS = {
    info:  '#5470ff',
    debug: '#94a3b8',
    ok:    '#22d97a',
    warn:  '#f59e0b',
    error: '#ff6f7a',
};

const StatusPill = ({ status }) => {
    const meta = {
        disconnected: { color: '#94a3b8', bg: '#f1f5f9', label: '○ disconnected' },
        connecting:   { color: '#f59e0b', bg: '#fffbeb', label: '◌ connecting…' },
        connected:    { color: '#10b981', bg: '#ecfdf5', label: '● connected' },
    }[status];
    return (
        <span style={{
            background: meta.bg, color: meta.color,
            padding: '4px 12px', borderRadius: 999,
            fontSize: 12, fontWeight: 800,
            border: `1px solid ${meta.color}33`,
            marginLeft: 6,
        }}>{meta.label}</span>
    );
};

const TokenInfo = ({ info }) => {
    let badge, color;
    if (!info.hasToken) {
        badge = 'Нет токена';
        color = '#ef4444';
    } else if (info.isGuest) {
        badge = 'Гость (не пустят)';
        color = '#f59e0b';
    } else {
        badge = `Юзер #${info.userId}`;
        color = '#10b981';
    }
    return (
        <label style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
            <span style={{
                fontSize: 11, color: KT.textSecondary,
                fontWeight: 800, textTransform: 'uppercase', letterSpacing: 0.8,
            }}>
                Токен (из localStorage)
            </span>
            <div style={{
                background: KT.bgSubtle,
                border: `1.5px solid ${KT.border}`,
                borderRadius: KT.radius,
                padding: '10px 14px',
                color: color,
                fontSize: 14, fontWeight: 800,
                fontFamily: KT.font,
                display: 'flex', alignItems: 'center', gap: 10,
            }}>
                <span style={{
                    width: 8, height: 8, borderRadius: '50%', background: color,
                }} />
                {badge}
            </div>
        </label>
    );
};

const Field = ({ label, value, onChange, placeholder, disabled }) => (
    <label style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
        <span style={{
            fontSize: 11, color: KT.textSecondary,
            fontWeight: 800, textTransform: 'uppercase', letterSpacing: 0.8,
        }}>
            {label}
        </span>
        <input
            value={value}
            onChange={(e) => onChange(e.target.value)}
            placeholder={placeholder}
            disabled={disabled}
            style={{
                background: KT.bgSubtle,
                border: `1.5px solid ${KT.border}`,
                borderRadius: KT.radius,
                padding: '10px 14px',
                color: KT.text,
                fontSize: 14, fontFamily: KT.font,
                outline: 'none',
                opacity: disabled ? 0.55 : 1,
                cursor: disabled ? 'not-allowed' : 'text',
            }}
        />
    </label>
);

const Btn = ({ children, onClick, disabled, primary }) => (
    <button
        onClick={onClick}
        disabled={disabled}
        style={{
            padding: '10px 18px',
            background: primary && !disabled
                ? KT.gradientBrand
                : (disabled ? KT.bgMuted : KT.bgPage),
            color: primary && !disabled ? '#fff' : KT.text,
            border: `1px solid ${primary && !disabled ? 'transparent' : KT.border}`,
            borderRadius: KT.radius,
            fontSize: 13, fontWeight: 800,
            cursor: disabled ? 'not-allowed' : 'pointer',
            opacity: disabled ? 0.55 : 1,
            fontFamily: KT.font,
            boxShadow: primary && !disabled ? KT.shadowAccent : 'none',
            transition: 'all 200ms cubic-bezier(0.34, 1.56, 0.64, 1)',
            letterSpacing: 0.2,
        }}
    >
        {children}
    </button>
);

const sectionTitleStyle = {
    margin: '0 0 14px', fontSize: 16, fontWeight: 800,
    color: KT.text, letterSpacing: -0.2,
    display: 'inline-flex', alignItems: 'center', gap: 8,
};

const cardStyle = {
    background: KT.bgPage,
    border: `1px solid ${KT.border}`,
    borderRadius: KT.radiusXl,
    padding: 24,
    boxShadow: KT.shadowMd,
};

const preStyle = {
    margin: 0,
    background: KT.bgSubtle,
    border: `1px solid ${KT.borderSoft}`,
    borderRadius: KT.radius,
    padding: 14,
    fontSize: 12.5,
    color: KT.text,
    fontFamily: 'Consolas, Menlo, monospace',
    overflowX: 'auto',
};

const pillStyle = {
    background: KT.gradientBrand,
    color: '#fff',
    padding: '2px 10px',
    borderRadius: 999,
    fontSize: 12,
    fontWeight: 800,
    marginLeft: 8,
};

export default RealtimeTest;
