const { useState, useEffect, useRef, useMemo, useCallback, createContext, useContext } = React;

const API = window.location.origin;

// ─── Helpers ──────────────────────────────────────────────────────────────────
function nameColor(name) {
  const palette = ['#f472b6','#60a5fa','#34d399','#fb923c','#a78bfa','#facc15','#f87171','#4ade80','#38bdf8','#e879f9'];
  let h = 0;
  for (const c of (name||'')) h = (h * 31 + c.charCodeAt(0)) & 0xfffffff;
  return palette[Math.abs(h) % palette.length];
}
function initials(name) { return (name||'??').slice(0,2).toUpperCase(); }
function fmtTime(ts) { return new Date(ts).toLocaleTimeString('ru-RU',{hour:'2-digit',minute:'2-digit'}); }
function fmtDate(ts) {
  const d = new Date(ts), today = new Date();
  if (d.toDateString() === today.toDateString()) return 'Сегодня';
  const yest = new Date(today); yest.setDate(today.getDate()-1);
  if (d.toDateString() === yest.toDateString()) return 'Вчера';
  return d.toLocaleDateString('ru-RU',{day:'numeric',month:'long'});
}
function fmtSize(bytes) {
  if (bytes < 1024) return bytes+'B';
  if (bytes < 1048576) return (bytes/1024).toFixed(1)+'KB';
  return (bytes/1048576).toFixed(1)+'MB';
}
function fmtDur(s) { return Math.floor(s/60)+':'+String(Math.floor(s%60)).padStart(2,'0'); }

async function apiFetch(path, opts={}, token) {
  const headers = { Authorization: 'Bearer '+(token||'') };
  let body = opts.body;
  if (body && !(body instanceof FormData)) { headers['Content-Type']='application/json'; body=JSON.stringify(body); }
  const res = await fetch(API+path, {...opts, headers, body});
  const data = await res.json().catch(()=>({}));
  if (!res.ok) throw new Error(data.error || res.statusText);
  return data;
}

function detectMime() {
  for (const t of ['audio/webm;codecs=opus','audio/webm','audio/ogg;codecs=opus','audio/ogg','audio/mp4'])
    if (MediaRecorder.isTypeSupported(t)) return t;
  return '';
}

// ─── App Context ──────────────────────────────────────────────────────────────
const Ctx = createContext({});
const useCtx = () => useContext(Ctx);

// ─── Auth Screen ──────────────────────────────────────────────────────────────
function AuthScreen({ onAuth }) {
  const [tab, setTab] = useState('login');
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const [email, setEmail] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);

  const submit = async (e) => {
    e.preventDefault(); setError(''); setLoading(true);
    try {
      const isReg = tab === 'register';
      const data = await apiFetch(isReg ? '/auth/register' : '/auth/login', {
        method: 'POST',
        body: isReg ? { username, password, email: email||undefined } : { username, password },
      });
      onAuth(data);
    } catch(err) { setError(err.message); }
    finally { setLoading(false); }
  };

  const inp = { display:'block', width:'100%', padding:'11px 14px', marginTop:8,
    background:'var(--bg-elev-2)', border:'1px solid var(--border)',
    borderRadius:10, fontSize:14, color:'var(--text)', outline:'none',
    fontFamily:'inherit' };

  return (
    <div style={{ minHeight:'100vh', display:'flex', alignItems:'center', justifyContent:'center', background:'var(--bg)' }}>
      <div style={{ width:360, maxWidth:'calc(100vw - 32px)', background:'var(--bg-elev)',
        border:'1px solid var(--border)', borderRadius:20, padding:'36px 28px',
        boxShadow:'0 20px 60px rgba(0,0,0,.4)' }}>
        <div style={{ fontFamily:'var(--font-serif)', fontSize:32, fontStyle:'italic',
          letterSpacing:'-0.02em', textAlign:'center', marginBottom:4 }}>чатАгенты</div>
        <div style={{ fontFamily:'var(--font-mono)', fontSize:11, color:'var(--text-3)',
          textAlign:'center', marginBottom:28 }}>real-time · AI-native</div>
        <div style={{ display:'flex', background:'var(--bg-elev-2)', borderRadius:10, padding:3,
          border:'1px solid var(--border)', marginBottom:20 }}>
          {['login','register'].map(t => (
            <button key={t} onClick={() => { setTab(t); setError(''); }} style={{
              flex:1, padding:'7px 0', borderRadius:8, fontSize:13, fontWeight:500,
              background: tab===t ? 'var(--text)' : 'transparent',
              color: tab===t ? 'var(--bg)' : 'var(--text-2)', transition:'all .15s',
            }}>{t==='login' ? 'Войти' : 'Регистрация'}</button>
          ))}
        </div>
        <form onSubmit={submit} style={{ marginTop:0 }}>
          <input value={username} onChange={e=>setUsername(e.target.value)}
            placeholder="Имя пользователя" required style={{ ...inp, marginTop:0 }}/>
          {tab==='register' && (
            <input value={email} onChange={e=>setEmail(e.target.value)}
              placeholder="Email (необязательно)" type="email" style={inp}/>
          )}
          <input value={password} onChange={e=>setPassword(e.target.value)}
            placeholder="Пароль" type="password" required style={inp}/>
          {error && <div style={{ color:'var(--danger)', fontSize:12, marginTop:8,
            fontFamily:'var(--font-mono)' }}>{error}</div>}
          <button type="submit" disabled={loading} style={{
            width:'100%', marginTop:16, padding:12, background:'var(--text)', color:'var(--bg)',
            borderRadius:10, fontSize:14, fontWeight:600, opacity:loading?.6:1,
          }}>{loading ? '…' : (tab==='login' ? 'Войти' : 'Создать аккаунт')}</button>
        </form>
      </div>
    </div>
  );
}

// ─── User Avatar ──────────────────────────────────────────────────────────────
function UserAvatar({ name, size=32, status=null, agent=false }) {
  const s = size;
  const fs = Math.max(10, Math.round(s*0.36));
  const color = nameColor(name);
  return (
    <div style={{ position:'relative', width:s, height:s, flexShrink:0 }}>
      <div style={{ width:s, height:s, borderRadius: agent ? '30%' : '50%',
        background:color, display:'flex', alignItems:'center', justifyContent:'center',
        fontSize:fs, fontWeight:700, color:'#0a0a0a', fontFamily:'var(--font-mono)',
        letterSpacing:'-0.02em', userSelect:'none' }}>
        {initials(name)}
      </div>
      {agent && (
        <div style={{ position:'absolute', bottom:-2, right:-2,
          width:Math.max(10,s*0.36), height:Math.max(10,s*0.36),
          borderRadius:4, background:'var(--bg)', border:'1.5px solid var(--bg)',
          display:'flex', alignItems:'center', justifyContent:'center' }}>
          <div style={{ width:'100%', height:'100%', borderRadius:3,
            background:'var(--text)', color:'var(--bg)',
            display:'flex', alignItems:'center', justifyContent:'center',
            fontFamily:'var(--font-mono)', fontSize:Math.max(6,s*0.22), fontWeight:700 }}>AI</div>
        </div>
      )}
      {status && !agent && (
        <div style={{ position:'absolute', bottom:0, right:0,
          width:Math.max(8,s*0.28), height:Math.max(8,s*0.28), borderRadius:'50%',
          background: status==='online' ? 'var(--online)' : status==='dnd' ? 'var(--danger)' : 'var(--text-3)',
          border:'2px solid var(--bg)' }}/>
      )}
    </div>
  );
}

// ─── Voice Player ─────────────────────────────────────────────────────────────
function VoicePlayer({ src }) {
  const [playing, setPlaying] = useState(false);
  const [progress, setProgress] = useState(0);
  const [duration, setDuration] = useState(0);
  const audioRef = useRef(null);

  useEffect(() => {
    const a = new Audio(src);
    audioRef.current = a;
    a.addEventListener('loadedmetadata', () => setDuration(a.duration||0));
    a.addEventListener('timeupdate', () => {
      const p = a.duration ? a.currentTime / a.duration : 0;
      setProgress(p);
    });
    a.addEventListener('ended', () => { setPlaying(false); setProgress(0); });
    return () => { a.pause(); };
  }, [src]);

  const toggle = () => {
    const a = audioRef.current;
    if (!a) return;
    if (playing) { a.pause(); setPlaying(false); }
    else { a.play(); setPlaying(true); }
  };

  const seek = (e) => {
    const a = audioRef.current;
    if (!a || !a.duration) return;
    const rect = e.currentTarget.getBoundingClientRect();
    a.currentTime = ((e.clientX - rect.left) / rect.width) * a.duration;
  };

  return (
    <div style={{ display:'flex', alignItems:'center', gap:10,
      padding:'10px 14px', borderRadius:12,
      background:'var(--bg-elev-2)', border:'1px solid var(--border)',
      width:260, maxWidth:'100%' }}>
      <button onClick={toggle} style={{ width:32, height:32, borderRadius:'50%',
        background:'var(--text)', color:'var(--bg)',
        display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0 }}>
        <Icon name={playing?'pause':'play'} size={14} stroke={0}/>
      </button>
      <div style={{ flex:1, display:'flex', flexDirection:'column', gap:5 }}>
        <div onClick={seek} style={{ height:4, background:'var(--border)', borderRadius:2, cursor:'pointer', position:'relative' }}>
          <div style={{ position:'absolute', left:0, top:0, height:'100%', width:(progress*100)+'%',
            background:'var(--text)', borderRadius:2, transition:'width .1s linear' }}/>
        </div>
        <div style={{ fontFamily:'var(--font-mono)', fontSize:10, color:'var(--text-3)' }}>
          {duration ? fmtDur(progress*duration)+' / '+fmtDur(duration) : '0:00'}
        </div>
      </div>
    </div>
  );
}

// ─── Image Lightbox ───────────────────────────────────────────────────────────
function Lightbox({ images, startIdx, onClose }) {
  const [idx, setIdx] = useState(startIdx);
  useEffect(() => {
    const handler = (e) => {
      if (e.key === 'Escape') onClose();
      if (e.key === 'ArrowLeft') setIdx(i => Math.max(0, i-1));
      if (e.key === 'ArrowRight') setIdx(i => Math.min(images.length-1, i+1));
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [images.length]);

  return (
    <div style={{ position:'fixed', inset:0, zIndex:300, display:'flex',
      alignItems:'center', justifyContent:'center' }}>
      <div onClick={onClose} style={{ position:'absolute', inset:0, background:'rgba(0,0,0,.9)', cursor:'zoom-out' }}/>
      <button onClick={onClose} style={{ position:'fixed', top:18, right:18,
        width:40, height:40, borderRadius:'50%', background:'rgba(255,255,255,.15)',
        color:'#fff', fontSize:20, zIndex:301, display:'flex', alignItems:'center', justifyContent:'center' }}>
        <Icon name="x" size={18}/>
      </button>
      {idx > 0 && (
        <button onClick={()=>setIdx(i=>i-1)} style={{ position:'fixed', left:14,
          top:'50%', transform:'translateY(-50%)', width:48, height:48, borderRadius:'50%',
          background:'rgba(255,255,255,.15)', color:'#fff', fontSize:30, zIndex:301,
          display:'flex', alignItems:'center', justifyContent:'center' }}>
          <Icon name="chevronLeft" size={28} stroke={2}/>
        </button>
      )}
      <img src={API+images[idx]} style={{ position:'relative', maxWidth:'90vw', maxHeight:'88vh',
        borderRadius:12, objectFit:'contain' }}/>
      {idx < images.length-1 && (
        <button onClick={()=>setIdx(i=>i+1)} style={{ position:'fixed', right:14,
          top:'50%', transform:'translateY(-50%)', width:48, height:48, borderRadius:'50%',
          background:'rgba(255,255,255,.15)', color:'#fff', zIndex:301,
          display:'flex', alignItems:'center', justifyContent:'center' }}>
          <Icon name="chevronRight" size={28} stroke={2}/>
        </button>
      )}
    </div>
  );
}

// ─── Message ──────────────────────────────────────────────────────────────────
function MessageRow({ msg, showHeader, onEdit, onDelete, onImgClick }) {
  const { myId } = useCtx();
  const isMe = msg.userId === myId || msg.user_id === myId;
  const isAgent = msg.isAgent;
  const name = msg.username || '?';
  const ts = msg.createdAt || msg.created_at;

  const renderContent = () => {
    if (msg.type === 'audio') return <VoicePlayer src={API + msg.file.url}/>;
    if (msg.type === 'image') return (
      <div onClick={()=>onImgClick && onImgClick(msg.file.url)}
        style={{ borderRadius:10, overflow:'hidden', maxWidth:280, cursor:'pointer',
          border:'1px solid var(--border)' }}>
        <img src={API+msg.file.url} style={{ width:'100%', display:'block', maxHeight:200, objectFit:'cover' }}/>
      </div>
    );
    if (msg.type === 'video') return (
      <video src={API+msg.file.url} controls style={{ maxWidth:280, borderRadius:10, display:'block' }}/>
    );
    if (msg.file) return (
      <a href={API+msg.file.url} download={msg.file.originalName} style={{
        display:'flex', alignItems:'center', gap:10, padding:'10px 14px',
        background:'var(--bg-elev-2)', border:'1px solid var(--border)',
        borderRadius:12, textDecoration:'none', color:'var(--text)', maxWidth:260 }}>
        <Icon name="file" size={20} style={{ color:'var(--text-3)', flexShrink:0 }}/>
        <div style={{ minWidth:0 }}>
          <div style={{ fontSize:13, fontWeight:500, overflow:'hidden', textOverflow:'ellipsis',
            whiteSpace:'nowrap', maxWidth:180 }}>{msg.file.originalName}</div>
          <div style={{ fontSize:11, color:'var(--text-3)' }}>скачать</div>
        </div>
      </a>
    );
    return (
      <div style={{ fontSize:14, lineHeight:1.55, color:'var(--text)',
        wordBreak:'break-word', whiteSpace:'pre-wrap' }}>
        {msg.text}
        {msg.editedAt && <span style={{ fontSize:11, color:'var(--text-3)', marginLeft:6,
          fontStyle:'italic' }}>изм.</span>}
      </div>
    );
  };

  return (
    <div className="msg-row" style={{ display:'flex', gap:12, padding: showHeader ? '6px 0' : '1px 0',
      position:'relative' }}>
      <div style={{ width:36, flexShrink:0 }}>
        {showHeader ? <UserAvatar name={name} size={36} agent={isAgent}/> : (
          <span className="msg-ts" style={{ fontFamily:'var(--font-mono)', fontSize:10,
            color:'var(--text-3)', display:'block', textAlign:'right', paddingTop:4 }}>
            {fmtTime(ts)}
          </span>
        )}
      </div>
      <div style={{ flex:1, minWidth:0 }}>
        {showHeader && (
          <div style={{ display:'flex', alignItems:'baseline', gap:8, marginBottom:2 }}>
            <span style={{ fontWeight:600, fontSize:14 }}>{name}</span>
            {isAgent && (
              <span style={{ display:'inline-flex', alignItems:'center', gap:3,
                padding:'1px 6px', borderRadius:4, background:'var(--bg-elev-2)',
                border:'1px solid var(--border)', fontFamily:'var(--font-mono)',
                fontSize:10, color:'var(--text-3)' }}>AI</span>
            )}
            <span style={{ fontFamily:'var(--font-mono)', fontSize:11, color:'var(--text-3)' }}>
              {fmtTime(ts)}
            </span>
          </div>
        )}
        {renderContent()}
      </div>
      {isMe && !isAgent && msg.type === 'text' && (
        <div className="msg-actions" style={{ position:'absolute', top:-12, right:8,
          display:'none', gap:2, background:'var(--bg-elev)', border:'1px solid var(--border)',
          borderRadius:8, padding:2, boxShadow:'0 4px 12px rgba(0,0,0,.3)' }}>
          <button onClick={()=>onEdit(msg)} className="msg-act-btn"
            style={{ padding:6, borderRadius:4, color:'var(--text-2)' }}
            title="Редактировать"><Icon name="more" size={14}/></button>
          <button onClick={()=>onDelete(msg)} className="msg-act-btn"
            style={{ padding:6, borderRadius:4, color:'var(--danger)' }}
            title="Удалить"><Icon name="x" size={14}/></button>
        </div>
      )}
    </div>
  );
}

// ─── Composer ─────────────────────────────────────────────────────────────────
function Composer({ roomId, socket, compact=false }) {
  const { token } = useCtx();
  const [text, setText] = useState('');
  const [recording, setRecording] = useState(false);
  const [voiceBlob, setVoiceBlob] = useState(null);
  const [voiceMime, setVoiceMime] = useState('');
  const [voiceUrl, setVoiceUrl] = useState('');
  const fileInputRef = useRef(null);
  const mediaRef = useRef(null);
  const chunksRef = useRef([]);
  const typingTimerRef = useRef(null);
  const isTypingRef = useRef(false);

  const sendTyping = (on) => {
    if (!socket || !roomId) return;
    if (on && !isTypingRef.current) {
      isTypingRef.current = true;
      socket.emit('typing:start', { roomId });
    }
    clearTimeout(typingTimerRef.current);
    if (on) {
      typingTimerRef.current = setTimeout(() => {
        isTypingRef.current = false;
        socket.emit('typing:stop', { roomId });
      }, 2000);
    } else {
      isTypingRef.current = false;
      socket.emit('typing:stop', { roomId });
    }
  };

  const sendText = () => {
    if (!text.trim() || !socket) return;
    socket.emit('message:send', { roomId, text: text.trim() });
    setText('');
    sendTyping(false);
  };

  const handleKey = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendText(); }
  };

  const handleTextChange = (e) => {
    setText(e.target.value);
    sendTyping(true);
  };

  const startRecording = async () => {
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const mime = detectMime();
      const mr = new MediaRecorder(stream, mime ? { mimeType: mime } : {});
      chunksRef.current = [];
      mr.ondataavailable = e => { if (e.data.size) chunksRef.current.push(e.data); };
      mr.onstop = () => {
        const blob = new Blob(chunksRef.current, { type: mime || 'audio/webm' });
        const url = URL.createObjectURL(blob);
        setVoiceBlob(blob); setVoiceMime(mime); setVoiceUrl(url);
        stream.getTracks().forEach(t => t.stop());
      };
      mr.start();
      mediaRef.current = mr;
      setRecording(true);
    } catch(err) { alert('Нет доступа к микрофону: ' + err.message); }
  };

  const stopRecording = () => {
    mediaRef.current?.stop();
    setRecording(false);
  };

  const sendVoice = async () => {
    if (!voiceBlob) return;
    const ext = voiceMime.includes('ogg') ? 'ogg' : voiceMime.includes('mp4') ? 'mp4' : 'webm';
    const formData = new FormData();
    formData.append('file', voiceBlob, `voice-${Date.now()}.${ext}`);
    try {
      const data = await apiFetch('/upload', { method:'POST', body:formData }, token);
      socket.emit('message:file', { roomId, fileId: data.fileId });
      setVoiceBlob(null); setVoiceUrl('');
    } catch(err) { alert('Ошибка отправки: '+err.message); }
  };

  const handleFile = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    e.target.value = '';
    const formData = new FormData();
    formData.append('file', file);
    try {
      const data = await apiFetch('/upload', { method:'POST', body:formData }, token);
      socket.emit('message:file', { roomId, fileId: data.fileId });
    } catch(err) { alert('Ошибка загрузки: '+err.message); }
  };

  if (voiceUrl) return (
    <div style={{ padding: compact ? '8px 12px' : '10px 16px',
      borderTop:'1px solid var(--border)', background:'var(--bg)',
      display:'flex', alignItems:'center', gap:10, flexShrink:0 }}>
      <audio src={voiceUrl} controls style={{ height:36 }}/>
      <button onClick={()=>{ setVoiceBlob(null); setVoiceUrl(''); }}
        style={{ color:'var(--danger)', padding:'6px 12px', borderRadius:8,
          background:'var(--bg-elev-2)', border:'1px solid var(--border)', fontSize:13 }}>✕</button>
      <button onClick={sendVoice}
        style={{ color:'var(--bg)', background:'var(--text)', padding:'6px 14px',
          borderRadius:8, fontSize:13, fontWeight:600 }}>Отправить</button>
    </div>
  );

  return (
    <div style={{ padding: compact ? '8px 12px' : '12px 16px',
      paddingBottom: compact ? 'max(8px, env(safe-area-inset-bottom))' : undefined,
      borderTop:'1px solid var(--border)', background:'var(--bg)', flexShrink:0 }}>
      <div className="composer-wrap" style={{ display:'flex', alignItems:'flex-end', gap:8,
        padding:'8px 10px', background:'var(--bg-elev)', border:'1px solid var(--border-strong)',
        borderRadius:12 }}>
        <button onClick={()=>fileInputRef.current?.click()}
          style={{ padding:6, color:'var(--text-3)', flexShrink:0 }} title="Файл">
          <Icon name="paperclip" size={18}/>
        </button>
        <textarea value={text} onChange={handleTextChange} onKeyDown={handleKey}
          placeholder="Сообщение…" rows={1}
          style={{ flex:1, background:'transparent', border:'none', outline:'none',
            resize:'none', color:'var(--text)', fontSize:14, lineHeight:1.5,
            padding:'4px 0', minHeight:24, maxHeight:140, fontFamily:'inherit' }}/>
        <button onClick={recording ? stopRecording : startRecording}
          style={{ padding:6, color: recording ? 'var(--danger)' : 'var(--text-3)',
            flexShrink:0, animation: recording ? 'pulse 1s infinite' : 'none' }}
          title={recording ? 'Остановить' : 'Голос'}>
          <Icon name="mic" size={18}/>
        </button>
        <button onClick={sendText} disabled={!text.trim()}
          style={{ padding:'6px 10px', borderRadius:8, flexShrink:0,
            background: text.trim() ? 'var(--text)' : 'var(--bg-elev-2)',
            color: text.trim() ? 'var(--bg)' : 'var(--text-3)',
            display:'flex', alignItems:'center', gap:4, fontSize:12, fontWeight:600,
            transition:'all .15s' }}>
          <Icon name="send" size={14} stroke={2}/>
        </button>
      </div>
      <input ref={fileInputRef} type="file" hidden onChange={handleFile}
        accept="image/*,video/*,audio/*,.pdf,.doc,.docx,.zip,.txt,.xls,.xlsx"/>
    </div>
  );
}

// ─── Sidebar ──────────────────────────────────────────────────────────────────
function Sidebar({ rooms, currentRoomId, onSelectRoom, onNewRoom, onDeleteRoom, onRenameRoom,
  onlineUsers, myName, myStatus, onStatusChange, onOpenAccount, collapsed=false, onClose }) {
  const [search, setSearch] = useState('');
  const filtered = rooms.filter(r => r.name.toLowerCase().includes(search.toLowerCase()));

  const statusColors = { online:'var(--online)', dnd:'var(--danger)', offline:'var(--text-3)' };

  return (
    <aside style={{ width: collapsed ? 0 : 272, flexShrink:0, background:'var(--bg-elev)',
      borderRight:'1px solid var(--border)', display:'flex', flexDirection:'column',
      height:'100%', overflow:'hidden', transition:'width .25s ease' }}>
      {/* Logo */}
      <div style={{ padding:'14px 16px', borderBottom:'1px solid var(--border)',
        display:'flex', alignItems:'center', gap:10, flexShrink:0 }}>
        <div style={{ width:28, height:28, borderRadius:8, background:'var(--text)', color:'var(--bg)',
          display:'flex', alignItems:'center', justifyContent:'center',
          fontFamily:'var(--font-serif)', fontSize:18, fontStyle:'italic', fontWeight:400, flexShrink:0 }}>ч</div>
        <div style={{ flex:1, minWidth:0 }}>
          <div style={{ fontSize:13, fontWeight:600, letterSpacing:'-0.01em' }}>чатАгенты</div>
          <div style={{ fontSize:10, color:'var(--text-3)', fontFamily:'var(--font-mono)' }}>real-time · AI-native</div>
        </div>
        {onClose && (
          <button onClick={onClose} style={{ color:'var(--text-3)', padding:4 }}>
            <Icon name="x" size={16}/>
          </button>
        )}
      </div>

      {/* Search */}
      <div style={{ padding:'10px 12px', flexShrink:0 }}>
        <div style={{ display:'flex', alignItems:'center', gap:8,
          padding:'7px 10px', background:'var(--bg-elev-2)',
          border:'1px solid var(--border)', borderRadius:8 }}>
          <Icon name="search" size={14} style={{ color:'var(--text-3)' }}/>
          <input value={search} onChange={e=>setSearch(e.target.value)} placeholder="Поиск"
            style={{ flex:1, background:'transparent', border:'none', outline:'none',
              color:'var(--text)', fontSize:13, fontFamily:'inherit' }}/>
        </div>
      </div>

      {/* Rooms */}
      <div style={{ flex:1, overflowY:'auto', padding:'4px 8px' }}>
        <div style={{ display:'flex', alignItems:'center', justifyContent:'space-between',
          padding:'10px 10px 6px' }}>
          <span style={{ fontFamily:'var(--font-mono)', fontSize:10, letterSpacing:'0.08em',
            textTransform:'uppercase', color:'var(--text-3)', fontWeight:500 }}>Каналы</span>
          <button onClick={onNewRoom} style={{ color:'var(--text-3)', padding:2, borderRadius:4 }}>
            <Icon name="plus" size={14}/>
          </button>
        </div>
        {filtered.map(room => {
          const active = String(room.id) === String(currentRoomId);
          return (
            <div key={room.id} onClick={()=>onSelectRoom(room)}
              className="nav-row" style={{
                display:'flex', alignItems:'center', gap:8,
                padding:'7px 10px', borderRadius:6, width:'100%',
                background: active ? 'var(--bg-active)' : 'transparent',
                color: active ? 'var(--text)' : 'var(--text-2)', marginBottom:1,
                fontWeight:500, position:'relative', cursor:'pointer',
              }}>
              <Icon name="hash" size={14} stroke={1.8} style={{ flexShrink:0 }}/>
              <span style={{ flex:1, textAlign:'left', fontSize:13,
                overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{room.name}</span>
              {room.name !== 'general' && (
                <button onClick={e=>{e.stopPropagation();onDeleteRoom(room.id)}}
                  className="room-del-btn"
                  style={{ color:'var(--text-3)', padding:'1px 3px', borderRadius:4,
                    opacity:0, fontSize:11, display:'flex', alignItems:'center' }}>
                  <Icon name="x" size={12}/>
                </button>
              )}
            </div>
          );
        })}

        {/* Online users */}
        {Object.values(onlineUsers).length > 0 && (
          <>
            <div style={{ padding:'14px 10px 6px' }}>
              <span style={{ fontFamily:'var(--font-mono)', fontSize:10, letterSpacing:'0.08em',
                textTransform:'uppercase', color:'var(--text-3)', fontWeight:500 }}>В сети</span>
            </div>
            {Object.values(onlineUsers).slice(0,8).map(u => (
              <div key={u.userId} style={{ display:'flex', alignItems:'center', gap:8,
                padding:'5px 10px', color:'var(--text-2)' }}>
                <div style={{ width:7, height:7, borderRadius:'50%', flexShrink:0,
                  background: u.status==='online' ? 'var(--online)' : u.status==='dnd' ? 'var(--danger)' : 'var(--text-3)' }}/>
                <span style={{ fontSize:13, overflow:'hidden', textOverflow:'ellipsis',
                  whiteSpace:'nowrap' }}>{u.username}</span>
              </div>
            ))}
          </>
        )}
      </div>

      {/* Me */}
      <div style={{ padding:'10px 12px', borderTop:'1px solid var(--border)',
        display:'flex', alignItems:'center', gap:10, flexShrink:0, position:'relative' }}>
        <div onClick={onOpenAccount} style={{ cursor:'pointer', flexShrink:0 }}>
          <UserAvatar name={myName} size={28} status={myStatus}/>
        </div>
        <div style={{ flex:1, minWidth:0 }}>
          <div style={{ fontSize:13, fontWeight:500, overflow:'hidden', textOverflow:'ellipsis',
            whiteSpace:'nowrap' }}>{myName}</div>
          <div style={{ fontSize:10, color: statusColors[myStatus] || 'var(--text-3)',
            fontFamily:'var(--font-mono)', display:'flex', alignItems:'center', gap:4 }}>
            <span style={{ width:5, height:5, borderRadius:'50%',
              background: statusColors[myStatus] || 'var(--text-3)' }}/>
            {myStatus === 'online' ? 'в сети' : myStatus === 'dnd' ? 'не беспокоить' : 'не в сети'}
          </div>
        </div>
        <StatusMenu status={myStatus} onChange={onStatusChange}/>
      </div>
    </aside>
  );
}

function StatusMenu({ status, onChange }) {
  const [open, setOpen] = useState(false);
  const opts = [
    { value:'online', label:'В сети', color:'var(--online)' },
    { value:'dnd', label:'Не беспокоить', color:'var(--danger)' },
    { value:'offline', label:'Не в сети', color:'var(--text-3)' },
  ];
  return (
    <div style={{ position:'relative' }}>
      <button onClick={()=>setOpen(!open)} style={{ padding:6, color:'var(--text-3)' }} title="Статус">
        <Icon name="settings" size={16}/>
      </button>
      {open && (
        <div style={{ position:'absolute', bottom:'100%', right:0, marginBottom:6,
          background:'var(--bg-elev)', border:'1px solid var(--border)', borderRadius:10,
          padding:4, boxShadow:'0 4px 20px rgba(0,0,0,.4)', minWidth:160, zIndex:50 }}>
          {opts.map(o => (
            <button key={o.value} onClick={()=>{ onChange(o.value); setOpen(false); }}
              style={{ display:'flex', alignItems:'center', gap:8, width:'100%',
                padding:'8px 10px', borderRadius:6, fontSize:13,
                background: status===o.value ? 'var(--bg-active)' : 'transparent',
                color:'var(--text)' }}>
              <span style={{ width:8, height:8, borderRadius:'50%', background:o.color, flexShrink:0 }}/>
              {o.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// ─── Chat Header ──────────────────────────────────────────────────────────────
function ChatHeader({ room, onToggleRight, onMenuClick, onRename }) {
  const [renaming, setRenaming] = useState(false);
  const [nameVal, setNameVal] = useState('');
  const { token } = useCtx();

  const startRename = () => {
    if (!room || room.name === 'general') return;
    setNameVal(room.name);
    setRenaming(true);
  };

  const saveRename = async () => {
    if (!nameVal.trim() || nameVal === room.name) { setRenaming(false); return; }
    try {
      await apiFetch(`/rooms/${room.id}/rename`, { method:'PUT', body:{ name:nameVal.trim() } }, token);
    } catch(err) { alert(err.message); }
    setRenaming(false);
  };

  if (!room) return (
    <header style={{ padding:'12px 20px', borderBottom:'1px solid var(--border)',
      display:'flex', alignItems:'center', gap:14, flexShrink:0, background:'var(--bg)',
      color:'var(--text-3)', fontSize:14 }}>
      {onMenuClick && (
        <button onClick={onMenuClick} style={{ padding:4, color:'var(--text-2)' }}>
          <Icon name="menu" size={18}/>
        </button>
      )}
      Выберите канал
    </header>
  );

  return (
    <header style={{ padding:'12px 20px', borderBottom:'1px solid var(--border)',
      display:'flex', alignItems:'center', gap:14, flexShrink:0, background:'var(--bg)' }}>
      {onMenuClick && (
        <button onClick={onMenuClick} style={{ padding:4, color:'var(--text-2)' }}>
          <Icon name="menu" size={18}/>
        </button>
      )}
      <div style={{ width:32, height:32, borderRadius:10,
        background:'var(--bg-elev-2)', border:'1px solid var(--border)',
        display:'flex', alignItems:'center', justifyContent:'center', color:'var(--text-2)', flexShrink:0 }}>
        <Icon name="hash" size={16} stroke={2}/>
      </div>
      <div style={{ flex:1, minWidth:0 }}>
        <div style={{ fontSize:15, fontWeight:600, letterSpacing:'-0.01em',
          display:'flex', alignItems:'center', gap:6 }}>
          {renaming ? (
            <input value={nameVal} onChange={e=>setNameVal(e.target.value)}
              onBlur={saveRename}
              onKeyDown={e=>{ if(e.key==='Enter') saveRename(); if(e.key==='Escape') setRenaming(false); }}
              autoFocus
              style={{ background:'transparent', border:'none', borderBottom:'2px solid var(--text)',
                outline:'none', fontSize:15, fontWeight:600, color:'var(--text)',
                fontFamily:'inherit', width:200 }}/>
          ) : (
            <span onDoubleClick={startRename} style={{ cursor: room.name!=='general' ? 'text' : 'default' }}>
              {room.name}
            </span>
          )}
          {room.name !== 'general' && !renaming && (
            <button onClick={startRename} className="header-edit-btn"
              style={{ color:'var(--text-3)', padding:2, opacity:0, transition:'opacity .15s' }}>
              <Icon name="more" size={13}/>
            </button>
          )}
        </div>
      </div>
      <button onClick={onToggleRight} style={{ padding:8, color:'var(--text-2)', borderRadius:6 }}>
        <Icon name="users" size={16}/>
      </button>
    </header>
  );
}

// ─── Message List ─────────────────────────────────────────────────────────────
function MessageList({ messages, roomId, socket, onEditMsg, onDeleteMsg }) {
  const [lightbox, setLightbox] = useState(null);
  const [editingId, setEditingId] = useState(null);
  const [editText, setEditText] = useState('');
  const scrollRef = useRef(null);
  const { token } = useCtx();

  useEffect(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [messages]);

  const imgs = messages.filter(m => m.type==='image' && m.file).map(m => m.file.url);

  const startEdit = (msg) => { setEditingId(msg.id); setEditText(msg.text); };
  const saveEdit = async (msg) => {
    if (!editText.trim() || editText === msg.text) { setEditingId(null); return; }
    try {
      await apiFetch(`/messages/${msg.id}`, { method:'PUT', body:{ text:editText.trim() } }, token);
    } catch(err) { alert(err.message); }
    setEditingId(null);
  };
  const doDelete = async (msg) => {
    if (!confirm('Удалить сообщение?')) return;
    try { await apiFetch(`/messages/${msg.id}`, { method:'DELETE' }, token); }
    catch(err) { alert(err.message); }
  };

  if (!messages.length) return (
    <div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center',
      color:'var(--text-3)', fontSize:14 }}>
      Нет сообщений. Напишите первым!
    </div>
  );

  const rows = [];
  messages.forEach((m, i) => {
    const prev = messages[i-1];
    const ts = m.createdAt || m.created_at;
    const prevTs = prev ? (prev.createdAt || prev.created_at) : null;
    if (!prev || fmtDate(prevTs) !== fmtDate(ts)) {
      rows.push({ kind:'date', date: fmtDate(ts), key:'d'+i });
    }
    const sameAuthor = prev && (prev.userId||prev.user_id) === (m.userId||m.user_id)
      && fmtDate(prevTs) === fmtDate(ts) && !prev.isAgent && !m.isAgent;
    rows.push({ kind:'msg', msg:m, showHeader:!sameAuthor, key:'m'+m.id });
  });

  return (
    <div ref={scrollRef} style={{ flex:1, overflowY:'auto', padding:'16px 20px' }}>
      {lightbox !== null && (
        <Lightbox images={imgs} startIdx={lightbox} onClose={()=>setLightbox(null)}/>
      )}
      {rows.map(row => {
        if (row.kind === 'date') return (
          <div key={row.key} style={{ display:'flex', alignItems:'center', gap:12, margin:'16px 0 8px' }}>
            <div style={{ flex:1, height:1, background:'var(--border)' }}/>
            <span style={{ fontFamily:'var(--font-mono)', fontSize:10, color:'var(--text-3)',
              letterSpacing:'0.08em', textTransform:'uppercase' }}>{row.date}</span>
            <div style={{ flex:1, height:1, background:'var(--border)' }}/>
          </div>
        );
        const m = row.msg;
        if (editingId === m.id) return (
          <div key={row.key} style={{ display:'flex', gap:12, padding:'4px 0' }}>
            <div style={{ width:36 }}/>
            <div style={{ flex:1 }}>
              <input value={editText} onChange={e=>setEditText(e.target.value)}
                onBlur={()=>saveEdit(m)}
                onKeyDown={e=>{ if(e.key==='Enter'){saveEdit(m);} if(e.key==='Escape') setEditingId(null); }}
                autoFocus
                style={{ width:'100%', padding:'7px 10px', background:'var(--bg-elev-2)',
                  border:'1.5px solid var(--text)', borderRadius:8, fontSize:14,
                  color:'var(--text)', outline:'none', fontFamily:'inherit' }}/>
            </div>
          </div>
        );
        return (
          <MessageRow key={row.key} msg={m} showHeader={row.showHeader}
            onEdit={startEdit} onDelete={doDelete}
            onImgClick={(url)=>setLightbox(imgs.indexOf(url))}/>
        );
      })}
    </div>
  );
}

// ─── Typing Bar ───────────────────────────────────────────────────────────────
function TypingBar({ typingUsers }) {
  const names = Object.values(typingUsers||{});
  if (!names.length) return null;
  const label = names.length === 1 ? `${names[0]} печатает…`
    : names.length === 2 ? `${names[0]} и ${names[1]} печатают…`
    : `${names[0]} и ещё ${names.length-1} печатают…`;
  return (
    <div style={{ padding:'4px 20px 6px', fontSize:12, color:'var(--text-3)',
      display:'flex', alignItems:'center', gap:8, flexShrink:0 }}>
      <span style={{ display:'flex', gap:3 }}>
        {[0,1,2].map(i=>(
          <span key={i} style={{ width:5, height:5, borderRadius:'50%', background:'var(--text-3)',
            animation:'typingDot 1.2s infinite', animationDelay:`${i*0.15}s` }}/>
        ))}
      </span>
      {label}
    </div>
  );
}

// ─── Right Panel ──────────────────────────────────────────────────────────────
function RightPanel({ room, onlineUsers }) {
  if (!room) return null;
  return (
    <aside style={{ width:260, flexShrink:0, borderLeft:'1px solid var(--border)',
      background:'var(--bg-elev)', overflowY:'auto', display:'flex', flexDirection:'column' }}>
      <div style={{ padding:'16px 16px 0' }}>
        <div style={{ fontFamily:'var(--font-mono)', fontSize:10, letterSpacing:'0.08em',
          textTransform:'uppercase', color:'var(--text-3)', marginBottom:10 }}>О канале</div>
        <div style={{ fontSize:15, fontWeight:600, marginBottom:12 }}>#{room.name}</div>
      </div>
      <div style={{ borderTop:'1px solid var(--border)', padding:16 }}>
        <div style={{ fontFamily:'var(--font-mono)', fontSize:10, letterSpacing:'0.08em',
          textTransform:'uppercase', color:'var(--text-3)', marginBottom:10 }}>
          В сети · {Object.values(onlineUsers).length}
        </div>
        {Object.values(onlineUsers).map(u => (
          <div key={u.userId} style={{ display:'flex', alignItems:'center', gap:10, padding:'5px 0' }}>
            <UserAvatar name={u.username} size={24} status={u.status}/>
            <span style={{ fontSize:13 }}>{u.username}</span>
          </div>
        ))}
      </div>
    </aside>
  );
}

// ─── New Room Modal ───────────────────────────────────────────────────────────
function NewRoomModal({ onClose, onCreate }) {
  const [name, setName] = useState('');
  const [loading, setLoading] = useState(false);
  const { token } = useCtx();

  const submit = async (e) => {
    e.preventDefault();
    if (!name.trim()) return;
    setLoading(true);
    try {
      await apiFetch('/rooms', { method:'POST', body:{ name:name.trim() } }, token);
      onClose();
    } catch(err) { alert(err.message); }
    finally { setLoading(false); }
  };

  return (
    <div style={{ position:'fixed', inset:0, background:'rgba(0,0,0,.5)', zIndex:200,
      display:'flex', alignItems:'center', justifyContent:'center' }}>
      <div style={{ background:'var(--bg-elev)', border:'1px solid var(--border)',
        borderRadius:16, padding:24, width:340, maxWidth:'calc(100vw-32px)',
        boxShadow:'0 20px 60px rgba(0,0,0,.4)' }}>
        <div style={{ fontSize:16, fontWeight:700, marginBottom:16 }}>Новый канал</div>
        <form onSubmit={submit}>
          <input value={name} onChange={e=>setName(e.target.value)}
            placeholder="название-канала" maxLength={32} autoFocus
            style={{ display:'block', width:'100%', padding:'10px 12px', marginBottom:16,
              background:'var(--bg-elev-2)', border:'1px solid var(--border)',
              borderRadius:8, fontSize:14, color:'var(--text)', outline:'none', fontFamily:'inherit' }}/>
          <div style={{ display:'flex', justifyContent:'flex-end', gap:8 }}>
            <button type="button" onClick={onClose}
              style={{ padding:'8px 16px', borderRadius:8, background:'var(--bg-elev-2)',
                border:'1px solid var(--border)', fontSize:13, color:'var(--text-2)' }}>Отмена</button>
            <button type="submit" disabled={!name.trim()||loading}
              style={{ padding:'8px 16px', borderRadius:8, background:'var(--text)',
                color:'var(--bg)', fontSize:13, fontWeight:600,
                opacity:!name.trim()||loading ? .6 : 1 }}>Создать</button>
          </div>
        </form>
      </div>
    </div>
  );
}

// ─── Account Page ─────────────────────────────────────────────────────────────
function AccountPage({ onBack, onLogout, myName, myStatus, onStatusChange }) {
  const { token } = useCtx();
  const [agentKeys, setAgentKeys] = useState([]);
  const [rooms, setRooms] = useState([]);
  const [newKeyName, setNewKeyName] = useState('');
  const [newKeyRoom, setNewKeyRoom] = useState('');
  const [newKey, setNewKey] = useState(null);

  useEffect(() => {
    apiFetch('/api/agent/keys', {}, token).then(setAgentKeys).catch(()=>{});
    apiFetch('/rooms', {}, token).then(data => {
      setRooms(data);
      if (data.length > 0) setNewKeyRoom(String(data[0].id));
    }).catch(()=>{});
  }, []);

  const createKey = async () => {
    if (!newKeyName.trim() || !newKeyRoom) return;
    try {
      const data = await apiFetch('/api/agent/keys', {
        method:'POST', body:{ name:newKeyName.trim(), roomId:Number(newKeyRoom) }
      }, token);
      setNewKey(data.key);
      const room = rooms.find(r => r.id === Number(newKeyRoom));
      setAgentKeys(prev => [...prev, { id:data.id, name:data.name, room_id:data.roomId, room_name:room?.name }]);
      setNewKeyName('');
    } catch(err) { alert(err.message); }
  };

  const deleteKey = async (id) => {
    if (!confirm('Удалить ключ?')) return;
    try {
      await apiFetch(`/api/agent/keys/${id}`, { method:'DELETE' }, token);
      setAgentKeys(prev => prev.filter(k => k.id !== id));
    } catch(err) { alert(err.message); }
  };

  const statusOpts = [
    { value:'online', label:'🟢 В сети' },
    { value:'dnd', label:'🔴 Не беспокоить' },
    { value:'offline', label:'⚫ Не в сети' },
  ];

  return (
    <div style={{ position:'fixed', inset:0, background:'var(--bg)', zIndex:100,
      overflowY:'auto', display:'flex', flexDirection:'column' }}>
      <div style={{ maxWidth:480, margin:'0 auto', padding:'20px 20px 40px', width:'100%' }}>
        <button onClick={onBack} style={{ color:'var(--text-2)', padding:'8px 0', marginBottom:20,
          fontSize:15, display:'flex', alignItems:'center', gap:6 }}>
          <Icon name="chevronLeft" size={18} stroke={2}/> Назад
        </button>

        <div style={{ textAlign:'center', marginBottom:24 }}>
          <UserAvatar name={myName} size={72}/>
          <div style={{ fontFamily:'var(--font-serif)', fontSize:26, fontStyle:'italic',
            marginTop:12, letterSpacing:'-0.02em' }}>{myName}</div>
        </div>

        {/* Status */}
        <div style={{ background:'var(--bg-elev)', border:'1px solid var(--border)',
          borderRadius:14, padding:16, marginBottom:14 }}>
          <div style={{ fontFamily:'var(--font-mono)', fontSize:10, textTransform:'uppercase',
            letterSpacing:'0.06em', color:'var(--text-3)', marginBottom:12 }}>Статус</div>
          <div style={{ display:'flex', gap:8, flexWrap:'wrap' }}>
            {statusOpts.map(o => (
              <button key={o.value} onClick={()=>onStatusChange(o.value)} style={{
                padding:'8px 14px', borderRadius:20, fontSize:13,
                border: myStatus===o.value ? '1.5px solid var(--text)' : '1.5px solid var(--border)',
                background: myStatus===o.value ? 'var(--bg-active)' : 'var(--bg-elev-2)',
                color: myStatus===o.value ? 'var(--text)' : 'var(--text-2)',
                fontWeight: myStatus===o.value ? 600 : 400,
              }}>{o.label}</button>
            ))}
          </div>
        </div>

        {/* Agent API Keys */}
        <div style={{ background:'var(--bg-elev)', border:'1px solid var(--border)',
          borderRadius:14, padding:16, marginBottom:14 }}>
          <div style={{ fontFamily:'var(--font-mono)', fontSize:10, textTransform:'uppercase',
            letterSpacing:'0.06em', color:'var(--text-3)', marginBottom:12 }}>Agent API Ключи</div>
          {newKey && (
            <div style={{ padding:'10px 12px', background:'var(--bg-elev-2)',
              border:'1px solid var(--border)', borderRadius:8, marginBottom:12 }}>
              <div style={{ fontSize:11, color:'var(--text-3)', marginBottom:4 }}>Скопируйте ключ (показывается один раз):</div>
              <div style={{ fontFamily:'var(--font-mono)', fontSize:12, color:'var(--text)',
                wordBreak:'break-all' }}>{newKey}</div>
              <button onClick={()=>{navigator.clipboard.writeText(newKey); alert('Скопировано!');}}
                style={{ marginTop:8, fontSize:12, color:'var(--text-2)', padding:'4px 8px',
                  background:'var(--bg)', border:'1px solid var(--border)', borderRadius:6 }}>Копировать</button>
            </div>
          )}
          {agentKeys.map(k => (
            <div key={k.id} style={{ display:'flex', alignItems:'center', gap:10,
              padding:'8px 0', borderBottom:'1px solid var(--border)' }}>
              <Icon name="key" size={14} style={{ color:'var(--text-3)', flexShrink:0 }}/>
              <div style={{ flex:1, minWidth:0 }}>
                <div style={{ fontSize:13 }}>{k.name}</div>
                {k.room_name && (
                  <div style={{ fontSize:11, color:'var(--text-3)', marginTop:2 }}>
                    # {k.room_name}
                  </div>
                )}
              </div>
              <button onClick={()=>deleteKey(k.id)} style={{ color:'var(--danger)', padding:4 }}>
                <Icon name="x" size={14}/>
              </button>
            </div>
          ))}
          <div style={{ display:'flex', flexDirection:'column', gap:8, marginTop:12 }}>
            <input value={newKeyName} onChange={e=>setNewKeyName(e.target.value)}
              placeholder="Название агента"
              style={{ padding:'8px 10px', background:'var(--bg-elev-2)',
                border:'1px solid var(--border)', borderRadius:8, fontSize:13,
                color:'var(--text)', outline:'none', fontFamily:'inherit' }}/>
            <div style={{ display:'flex', gap:8 }}>
              <select value={newKeyRoom} onChange={e=>setNewKeyRoom(e.target.value)}
                style={{ flex:1, padding:'8px 10px', background:'var(--bg-elev-2)',
                  border:'1px solid var(--border)', borderRadius:8, fontSize:13,
                  color:'var(--text)', outline:'none', fontFamily:'inherit' }}>
                {rooms.map(r => (
                  <option key={r.id} value={r.id}># {r.name}</option>
                ))}
              </select>
              <button onClick={createKey} disabled={!newKeyName.trim() || !newKeyRoom}
                style={{ padding:'8px 14px', borderRadius:8, background:'var(--text)',
                  color:'var(--bg)', fontSize:13, fontWeight:600,
                  opacity:(!newKeyName.trim() || !newKeyRoom)?0.6:1 }}>Создать</button>
            </div>
          </div>
          <div style={{ marginTop:12, padding:'10px 12px', background:'var(--bg-elev-2)',
            borderRadius:8, fontFamily:'var(--font-mono)', fontSize:11, color:'var(--text-3)',
            lineHeight:1.6 }}>
            <div style={{ fontWeight:600, color:'var(--text-2)', marginBottom:4 }}>API для агентов:</div>
            <div>GET /api/agent/rooms</div>
            <div>GET /api/agent/messages/:roomId?after=id</div>
            <div>POST /api/agent/reply {'{'} roomId, text {'}'}</div>
            <div style={{ marginTop:4 }}>Заголовок: X-Agent-Key: {'<ключ>'}</div>
          </div>
        </div>

        <button onClick={onLogout} style={{ width:'100%', padding:12,
          border:'1.5px solid var(--danger)', borderRadius:12, background:'transparent',
          color:'var(--danger)', fontSize:14, fontWeight:600 }}>Выйти из аккаунта</button>
      </div>
    </div>
  );
}

// ─── Mobile Layout ────────────────────────────────────────────────────────────
function MobileLayout({ rooms, currentRoomId, messages, typingUsers, onlineUsers,
  myName, myStatus, socket, onSelectRoom, onNewRoom, onDeleteRoom, onStatusChange, onLogout }) {
  const [tab, setTab] = useState('home');
  const [showAccount, setShowAccount] = useState(false);

  if (showAccount) return (
    <AccountPage onBack={()=>setShowAccount(false)} onLogout={onLogout}
      myName={myName} myStatus={myStatus} onStatusChange={onStatusChange}/>
  );

  if (currentRoomId) {
    const room = rooms.find(r => String(r.id) === String(currentRoomId));
    return (
      <div style={{ display:'flex', flexDirection:'column', height:'100%', background:'var(--bg)' }}>
        <header style={{ display:'flex', alignItems:'center', gap:10,
          padding:'10px 14px', borderBottom:'1px solid var(--border)', flexShrink:0 }}>
          <button onClick={()=>onSelectRoom(null)} style={{ padding:6, color:'var(--text)' }}>
            <Icon name="chevronLeft" size={22} stroke={2}/>
          </button>
          <div style={{ width:32, height:32, borderRadius:10, background:'var(--bg-elev-2)',
            border:'1px solid var(--border)', display:'flex', alignItems:'center',
            justifyContent:'center', color:'var(--text-2)', flexShrink:0 }}>
            <Icon name="hash" size={16} stroke={2}/>
          </div>
          <div style={{ flex:1, minWidth:0 }}>
            <div style={{ fontSize:15, fontWeight:600, overflow:'hidden',
              textOverflow:'ellipsis', whiteSpace:'nowrap' }}>{room?.name}</div>
          </div>
        </header>
        <MessageList messages={messages[currentRoomId]||[]} roomId={currentRoomId} socket={socket}/>
        <TypingBar typingUsers={typingUsers[currentRoomId]||{}}/>
        <Composer roomId={currentRoomId} socket={socket} compact/>
      </div>
    );
  }

  const tabs = [
    { id:'home', icon:'home', label:'Чаты' },
    { id:'channels', icon:'hash', label:'Каналы' },
    { id:'profile', icon:'user', label:'Профиль' },
  ];

  return (
    <div style={{ display:'flex', flexDirection:'column', height:'100%', background:'var(--bg)' }}>
      <div style={{ flex:1, minHeight:0, overflowY:'auto' }}>
        {tab === 'home' && (
          <div>
            <div style={{ padding:'12px 20px 8px' }}>
              <div style={{ fontFamily:'var(--font-serif)', fontSize:32, fontStyle:'italic',
                letterSpacing:'-0.02em' }}>чаты</div>
            </div>
            {rooms.map(r => <MobileRoomRow key={r.id} room={r} messages={messages} onSelect={()=>onSelectRoom(r.id)}/>)}
          </div>
        )}
        {tab === 'channels' && (
          <div>
            <div style={{ padding:'12px 20px 8px' }}>
              <div style={{ fontFamily:'var(--font-serif)', fontSize:32, fontStyle:'italic',
                letterSpacing:'-0.02em' }}>каналы</div>
            </div>
            <div style={{ padding:'0 16px 12px' }}>
              <button onClick={onNewRoom} style={{ width:'100%', display:'flex', alignItems:'center',
                justifyContent:'center', gap:8, padding:12, borderRadius:12,
                background:'var(--text)', color:'var(--bg)', fontSize:14, fontWeight:600 }}>
                <Icon name="plus" size={16} stroke={2}/> Новый канал
              </button>
            </div>
            {rooms.map(r => <MobileRoomRow key={r.id} room={r} messages={messages} onSelect={()=>onSelectRoom(r.id)}/>)}
          </div>
        )}
        {tab === 'profile' && (
          <AccountPage onBack={()=>setTab('home')} onLogout={onLogout}
            myName={myName} myStatus={myStatus} onStatusChange={onStatusChange}/>
        )}
      </div>
      <nav style={{ display:'flex', padding:'8px 4px', paddingBottom:'max(14px, env(safe-area-inset-bottom))',
        borderTop:'1px solid var(--border)', background:'var(--bg)', flexShrink:0 }}>
        {tabs.map(t => {
          const active = tab === t.id;
          return (
            <button key={t.id} onClick={()=>setTab(t.id)} style={{
              flex:1, display:'flex', flexDirection:'column', alignItems:'center', gap:3,
              padding:'6px 4px', color: active ? 'var(--text)' : 'var(--text-3)', position:'relative',
            }}>
              {active && <span style={{ position:'absolute', top:-8, left:'50%', transform:'translateX(-50%)',
                width:24, height:2, borderRadius:2, background:'var(--text)' }}/>}
              <Icon name={t.icon} size={22} stroke={active ? 2 : 1.6}/>
              <span style={{ fontSize:10, fontWeight: active ? 600 : 500,
                fontFamily:'var(--font-mono)', letterSpacing:'0.02em' }}>{t.label}</span>
            </button>
          );
        })}
      </nav>
    </div>
  );
}

function MobileRoomRow({ room, messages, onSelect }) {
  const msgs = messages[room.id] || [];
  const last = msgs[msgs.length-1];
  return (
    <button onClick={onSelect} className="mob-row" style={{ display:'flex', alignItems:'center',
      gap:12, padding:'10px 20px', width:'100%', textAlign:'left', background:'transparent' }}>
      <div style={{ width:44, height:44, borderRadius:14, background:'var(--bg-elev-2)',
        border:'1px solid var(--border)', display:'flex', alignItems:'center',
        justifyContent:'center', color:'var(--text-2)', flexShrink:0 }}>
        <Icon name="hash" size={20} stroke={1.8}/>
      </div>
      <div style={{ flex:1, minWidth:0 }}>
        <div style={{ display:'flex', alignItems:'center', gap:6, marginBottom:2 }}>
          <span style={{ fontSize:15, fontWeight:600, overflow:'hidden',
            textOverflow:'ellipsis', whiteSpace:'nowrap', flex:1 }}>{room.name}</span>
          {last && <span style={{ fontFamily:'var(--font-mono)', fontSize:10,
            color:'var(--text-3)', flexShrink:0 }}>{fmtTime(last.createdAt||last.created_at)}</span>}
        </div>
        {last && (
          <div style={{ fontSize:13, color:'var(--text-2)', overflow:'hidden',
            textOverflow:'ellipsis', whiteSpace:'nowrap' }}>
            {last.username}: {last.text || '(вложение)'}
          </div>
        )}
      </div>
    </button>
  );
}

// ─── Desktop Layout ───────────────────────────────────────────────────────────
function DesktopLayout({ rooms, currentRoomId, messages, typingUsers, onlineUsers,
  myName, myStatus, socket, onSelectRoom, onNewRoom, onDeleteRoom, onRenameRoom,
  onStatusChange, onLogout }) {
  const [showRight, setShowRight] = useState(true);
  const [showAccount, setShowAccount] = useState(false);

  const currentRoom = rooms.find(r => String(r.id) === String(currentRoomId));

  if (showAccount) return (
    <AccountPage onBack={()=>setShowAccount(false)} onLogout={onLogout}
      myName={myName} myStatus={myStatus} onStatusChange={onStatusChange}/>
  );

  return (
    <div style={{ display:'flex', height:'100%', background:'var(--bg)' }}>
      <Sidebar rooms={rooms} currentRoomId={currentRoomId} onSelectRoom={r=>onSelectRoom(r.id)}
        onNewRoom={onNewRoom} onDeleteRoom={onDeleteRoom} onRenameRoom={onRenameRoom}
        onlineUsers={onlineUsers} myName={myName} myStatus={myStatus}
        onStatusChange={onStatusChange} onOpenAccount={()=>setShowAccount(true)}/>
      <main style={{ flex:1, minWidth:0, display:'flex', flexDirection:'column' }}>
        <ChatHeader room={currentRoom} onToggleRight={()=>setShowRight(!showRight)}/>
        {currentRoom ? (
          <>
            <MessageList messages={messages[currentRoomId]||[]} roomId={currentRoomId} socket={socket}/>
            <TypingBar typingUsers={typingUsers[currentRoomId]||{}}/>
            <Composer roomId={currentRoomId} socket={socket}/>
          </>
        ) : (
          <div style={{ flex:1, display:'flex', alignItems:'center', justifyContent:'center',
            color:'var(--text-3)', fontSize:14 }}>Выберите канал</div>
        )}
      </main>
      {showRight && <RightPanel room={currentRoom} onlineUsers={onlineUsers}/>}
    </div>
  );
}

// ─── Main Chat App ────────────────────────────────────────────────────────────
function ChatApp({ token, myId, myName, onLogout }) {
  const [rooms, setRooms] = useState([]);
  const [currentRoomId, setCurrentRoomId] = useState(null);
  const [messages, setMessages] = useState({});
  const [typingUsers, setTypingUsers] = useState({});
  const [onlineUsers, setOnlineUsers] = useState({});
  const [myStatus, setMyStatus] = useState(localStorage.getItem('myStatus')||'online');
  const [socket, setSocket] = useState(null);
  const [showNewRoom, setShowNewRoom] = useState(false);
  const [viewport, setViewport] = useState(window.innerWidth < 820 ? 'mobile' : 'desktop');

  useEffect(() => {
    const onResize = () => setViewport(window.innerWidth < 820 ? 'mobile' : 'desktop');
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);

  // Load rooms
  useEffect(() => {
    apiFetch('/rooms', {}, token).then(setRooms).catch(console.error);
  }, [token]);

  // Socket
  useEffect(() => {
    const sock = io(API, { auth: { token } });

    sock.on('room:created', room => setRooms(prev => [...prev, room]));
    sock.on('room:deleted', ({ roomId }) => {
      setRooms(prev => prev.filter(r => String(r.id) !== String(roomId)));
      setCurrentRoomId(prev => String(prev) === String(roomId) ? null : prev);
    });
    sock.on('room:renamed', ({ roomId, name }) =>
      setRooms(prev => prev.map(r => String(r.id)===String(roomId) ? {...r,name} : r)));

    sock.on('message:new', msg => {
      const rId = String(msg.roomId);
      setMessages(prev => ({ ...prev, [rId]: [...(prev[rId]||[]), msg] }));
    });
    sock.on('room:history', msgs => {
      if (!msgs.length) return;
      const rId = String(msgs[0]?.room_id || msgs[0]?.roomId);
      if (rId) setMessages(prev => ({ ...prev, [rId]: msgs }));
    });
    sock.on('message:edited', ({ id, roomId, text, editedAt }) => {
      const rId = String(roomId);
      setMessages(prev => ({
        ...prev,
        [rId]: (prev[rId]||[]).map(m => m.id===id ? {...m,text,editedAt} : m),
      }));
    });
    sock.on('message:deleted', ({ messageId, roomId }) => {
      const rId = String(roomId);
      setMessages(prev => ({
        ...prev,
        [rId]: (prev[rId]||[]).filter(m => m.id !== messageId),
      }));
    });

    sock.on('user:online', u => setOnlineUsers(prev => ({ ...prev, [u.userId]: u })));
    sock.on('user:offline', ({ userId }) => setOnlineUsers(prev => {
      const next = {...prev}; delete next[userId]; return next;
    }));
    sock.on('user:status', u => setOnlineUsers(prev =>
      prev[u.userId] ? { ...prev, [u.userId]: {...prev[u.userId], status:u.status} } : prev));

    sock.on('typing:start', ({ userId, username, roomId }) => {
      const rId = String(roomId);
      setTypingUsers(prev => ({ ...prev, [rId]: { ...(prev[rId]||{}), [userId]: username } }));
    });
    sock.on('typing:stop', ({ userId, roomId }) => {
      const rId = String(roomId);
      setTypingUsers(prev => {
        const t = { ...(prev[rId]||{}) }; delete t[userId];
        return { ...prev, [rId]: t };
      });
    });

    setSocket(sock);
    return () => sock.disconnect();
  }, [token]);

  // Join room on select
  useEffect(() => {
    if (socket && currentRoomId) socket.emit('room:join', currentRoomId);
  }, [socket, currentRoomId]);

  const selectRoom = (id) => setCurrentRoomId(id ? String(id) : null);

  const handleStatus = async (status) => {
    try {
      await apiFetch('/user/status', { method:'PUT', body:{ status } }, token);
      setMyStatus(status);
      localStorage.setItem('myStatus', status);
    } catch(err) { console.error(err); }
  };

  const deleteRoom = async (roomId) => {
    if (!confirm('Удалить комнату?')) return;
    try { await apiFetch(`/rooms/${roomId}`, { method:'DELETE' }, token); }
    catch(err) { alert(err.message); }
  };

  const ctx = { token, myId };

  const sharedProps = {
    rooms, currentRoomId, messages, typingUsers, onlineUsers,
    myName, myStatus, socket,
    onSelectRoom: selectRoom,
    onNewRoom: () => setShowNewRoom(true),
    onDeleteRoom: deleteRoom,
    onRenameRoom: () => {},
    onStatusChange: handleStatus,
    onLogout,
  };

  return (
    <Ctx.Provider value={ctx}>
      <div style={{ height:'100dvh', overflow:'hidden', background:'var(--bg)' }}>
        {viewport === 'mobile'
          ? <MobileLayout {...sharedProps}/>
          : <DesktopLayout {...sharedProps}/>}
        {showNewRoom && <NewRoomModal onClose={()=>setShowNewRoom(false)} onCreate={()=>{}}/>}
      </div>
    </Ctx.Provider>
  );
}

// ─── Root ─────────────────────────────────────────────────────────────────────
function App() {
  const [auth, setAuth] = useState(() => {
    const token = localStorage.getItem('token');
    const myId  = Number(localStorage.getItem('myId'));
    const myName = localStorage.getItem('myName');
    return token && myId ? { token, myId, myName } : null;
  });

  const handleAuth = (data) => {
    localStorage.setItem('token',  data.token);
    localStorage.setItem('myId',   data.userId);
    localStorage.setItem('myName', data.username);
    setAuth({ token: data.token, myId: data.userId, myName: data.username });
  };

  const handleLogout = () => {
    localStorage.clear();
    setAuth(null);
  };

  if (!auth) return <AuthScreen onAuth={handleAuth}/>;
  return <ChatApp token={auth.token} myId={auth.myId} myName={auth.myName} onLogout={handleLogout}/>;
}

ReactDOM.createRoot(document.getElementById('root')).render(<App/>);
