// Max DM — Telegram MiniApp
const { useState, useEffect, useRef, useCallback } = React;

// ── Telegram WebApp ────────────────────────────────────────────────────────────
const tg = window.Telegram?.WebApp || {};
if (tg.expand) {
  tg.expand();
  if (tg.enableClosingConfirmation) tg.enableClosingConfirmation();
}

const API_BASE = window.location.origin + '/api/v1';
const IS_DEV = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';

function getInitData() {
  return tg.initData || '';
}

async function apiRequest(endpoint, options = {}) {
  const headers = {
    'X-Init-Data': getInitData(),
    'Content-Type': 'application/json',
    ...options.headers,
  };
  const response = await fetch(API_BASE + endpoint, { ...options, headers });
  if (!response.ok) throw new Error(`API error: ${response.status}`);
  return response.json();
}

// ── Theme ──────────────────────────────────────────────────────────────────────
function makeTheme(dark) {
  const accentHue = 25;
  const radius = 14;
  const accent      = `oklch(0.7  0.18 ${accentHue})`;
  const accentSoft  = dark ? `oklch(0.32 0.1  ${accentHue})` : `oklch(0.94 0.07 ${accentHue})`;
  const accentDeep  = `oklch(0.55 0.17 ${accentHue})`;

  if (dark) return {
    accent, accentSoft, accentDeep,
    bg:            'oklch(0.18 0.008 60)',
    surface:       'oklch(0.22 0.008 60)',
    surfaceElev:   'oklch(0.26 0.008 60)',
    text:          'oklch(0.97 0.005 70)',
    textMuted:     'oklch(0.7  0.01  70)',
    textFaint:     'oklch(0.5  0.01  70)',
    stroke:        'oklch(0.3  0.008 60)',
    bubbleIn:      'oklch(0.27 0.008 60)',
    bubbleOut:     accent,
    bubbleOutText: 'oklch(0.99 0.005 70)',
    radius, isDark: true,
  };

  return {
    accent, accentSoft, accentDeep,
    bg:            'oklch(0.985 0.005 75)',
    surface:       'oklch(1 0 0)',
    surfaceElev:   'oklch(0.97  0.005 75)',
    text:          'oklch(0.18  0.008 60)',
    textMuted:     'oklch(0.45  0.01  70)',
    textFaint:     'oklch(0.65  0.01  70)',
    stroke:        'oklch(0.92  0.008 70)',
    bubbleIn:      'oklch(0.95  0.005 75)',
    bubbleOut:     accent,
    bubbleOutText: 'oklch(0.99  0.005 70)',
    radius, isDark: false,
  };
}

// ── Utilities ──────────────────────────────────────────────────────────────────
function formatChatTime(ts) {
  const d = new Date(ts), now = new Date();
  if (d.toDateString() === now.toDateString())
    return d.toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' });
  const yest = new Date(now); yest.setDate(yest.getDate() - 1);
  if (d.toDateString() === yest.toDateString()) return 'вчера';
  if ((now - d) / 86400000 < 7)
    return d.toLocaleDateString('ru-RU', { weekday: 'short' });
  return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'short' });
}

function formatMsgTime(ts) {
  return new Date(ts).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' });
}

function formatDayHeader(ts) {
  const d = new Date(ts), now = new Date();
  if (d.toDateString() === now.toDateString()) return 'Сегодня';
  const yest = new Date(now); yest.setDate(yest.getDate() - 1);
  if (d.toDateString() === yest.toDateString()) return 'Вчера';
  return d.toLocaleDateString('ru-RU', { day: 'numeric', month: 'long' });
}

function hueFromKey(key) {
  const s = String(key);
  let h = 0;
  for (let i = 0; i < s.length; i++) h = ((h << 5) - h + s.charCodeAt(i)) | 0;
  return Math.abs(h) % 360;
}

// ── Avatar ─────────────────────────────────────────────────────────────────────
function Avatar({ name, id, size = 48, dark = true }) {
  const initial = (name || '?').charAt(0).toUpperCase();
  const hue = hueFromKey(id ?? name ?? '');
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', flexShrink: 0,
      background: dark ? `oklch(0.42 0.08 ${hue})` : `oklch(0.88 0.06 ${hue})`,
      color:      dark ? `oklch(0.92 0.05 ${hue})` : `oklch(0.35 0.08 ${hue})`,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontSize: size * 0.42, fontWeight: 600,
      letterSpacing: '-0.01em', userSelect: 'none',
    }}>
      {initial}
    </div>
  );
}

// ── Loading screen ─────────────────────────────────────────────────────────────
function LoadingScreen({ theme: T }) {
  return (
    <div style={{
      height: '100%', display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
      background: T.bg, color: T.text, padding: 24,
    }}>
      <div style={{ position: 'relative', width: 56, height: 56, marginBottom: 18 }}>
        <div style={{
          position: 'absolute', inset: 0, borderRadius: '50%',
          border: `3px solid ${T.surfaceElev}`,
        }} />
        <div style={{
          position: 'absolute', inset: 0, borderRadius: '50%',
          border: '3px solid transparent', borderTopColor: T.accent,
          animation: 'mxd-spin 0.9s linear infinite',
        }} />
      </div>
      <div style={{ fontSize: 16, fontWeight: 600 }}>Подключаемся…</div>
      <div style={{ fontSize: 13, color: T.textMuted, marginTop: 6 }}>Загружаем ваши диалоги</div>
    </div>
  );
}

// ── Empty screen ───────────────────────────────────────────────────────────────
function EmptyScreen({ theme: T }) {
  return (
    <div style={{
      height: '100%', display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
      background: T.bg, color: T.text, padding: '24px 32px', textAlign: 'center',
    }}>
      <div style={{
        width: 84, height: 84, borderRadius: 28, background: T.surface,
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        marginBottom: 20, position: 'relative',
      }}>
        <svg width="40" height="40" viewBox="0 0 40 40" fill="none">
          <path d="M8 14C8 11.2 10.2 9 13 9H27C29.8 9 32 11.2 32 14V22C32 24.8 29.8 27 27 27H18L12 31V27C9.8 26.4 8 24.4 8 22V14Z"
                stroke={T.textMuted} strokeWidth="2.2" strokeLinejoin="round"/>
        </svg>
        <div style={{
          position: 'absolute', top: -4, right: -4,
          width: 22, height: 22, borderRadius: '50%',
          background: T.accent, color: T.bubbleOutText,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          boxShadow: `0 4px 10px -2px ${T.accent}`,
        }}>
          <svg width="10" height="10" viewBox="0 0 10 10" fill="none">
            <path d="M5 1V9M1 5H9" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
          </svg>
        </div>
      </div>
      <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700, letterSpacing: '-0.01em' }}>Пока тихо</h2>
      <p style={{ margin: '10px 0 0', color: T.textMuted, fontSize: 15, lineHeight: 1.45, maxWidth: 280 }}>
        Когда вам напишут, диалоги появятся здесь
      </p>
    </div>
  );
}

// ── Error screen ───────────────────────────────────────────────────────────────
function ErrorScreen({ theme: T, message, onRetry }) {
  return (
    <div style={{
      height: '100%', display: 'flex', flexDirection: 'column',
      alignItems: 'center', justifyContent: 'center',
      background: T.bg, color: T.text, padding: '24px 32px', textAlign: 'center',
    }}>
      <div style={{
        width: 84, height: 84, borderRadius: 28, background: T.surface,
        display: 'flex', alignItems: 'center', justifyContent: 'center', marginBottom: 20,
      }}>
        <svg width="40" height="40" viewBox="0 0 40 40" fill="none">
          <circle cx="20" cy="20" r="14" stroke={T.textMuted} strokeWidth="2.2"/>
          <path d="M20 13V22" stroke={T.textMuted} strokeWidth="2.2" strokeLinecap="round"/>
          <circle cx="20" cy="27" r="1.4" fill={T.textMuted}/>
        </svg>
      </div>
      <h2 style={{ margin: 0, fontSize: 20, fontWeight: 700, letterSpacing: '-0.01em' }}>
        Что-то пошло не так
      </h2>
      <p style={{ margin: '10px 0 0', color: T.textMuted, fontSize: 15, lineHeight: 1.45, maxWidth: 280 }}>
        {message}
      </p>
      {onRetry && (
        <button onClick={onRetry} style={{
          marginTop: 24, height: 44, padding: '0 32px',
          borderRadius: T.radius + 4, background: T.accent,
          color: T.bubbleOutText, border: 'none',
          fontSize: 15, fontWeight: 600, cursor: 'pointer',
        }}>
          Попробовать снова
        </button>
      )}
    </div>
  );
}

// ── Chat list ──────────────────────────────────────────────────────────────────
function ChatList({ theme: T, chats, onOpenChat }) {
  const [searchActive, setSearchActive] = useState(false);
  const [query, setQuery] = useState('');

  const filtered = query
    ? chats.filter(c =>
        c.chat_name.toLowerCase().includes(query.toLowerCase()) ||
        (c.last_message_text || '').toLowerCase().includes(query.toLowerCase()))
    : chats;

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.bg, color: T.text }}>
      {/* Header */}
      <div style={{ padding: '16px 20px 12px', background: T.bg, position: 'sticky', top: 0, zIndex: 5 }}>
        {!searchActive ? (
          <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between' }}>
            <div>
              <div style={{ fontSize: 12, fontWeight: 600, color: T.textFaint, letterSpacing: '0.06em', textTransform: 'uppercase' }}>
                Max DM
              </div>
              <h1 style={{ margin: '4px 0 0', fontSize: 30, fontWeight: 700, letterSpacing: '-0.025em', lineHeight: 1.1 }}>
                Чаты
              </h1>
            </div>
            <button onClick={() => setSearchActive(true)} aria-label="Поиск" style={{
              width: 40, height: 40, borderRadius: 20,
              background: T.surface, border: 'none', color: T.text,
              display: 'flex', alignItems: 'center', justifyContent: 'center', cursor: 'pointer',
            }}>
              <svg width="18" height="18" viewBox="0 0 18 18" fill="none">
                <circle cx="8" cy="8" r="6" stroke="currentColor" strokeWidth="2"/>
                <path d="M13 13L16 16" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
              </svg>
            </button>
          </div>
        ) : (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10, paddingTop: 4 }}>
            <div style={{
              flex: 1, display: 'flex', alignItems: 'center', gap: 8,
              height: 40, padding: '0 12px',
              background: T.surface, borderRadius: T.radius + 4,
            }}>
              <svg width="16" height="16" viewBox="0 0 18 18" fill="none">
                <circle cx="8" cy="8" r="6" stroke={T.textMuted} strokeWidth="2"/>
                <path d="M13 13L16 16" stroke={T.textMuted} strokeWidth="2" strokeLinecap="round"/>
              </svg>
              <input
                autoFocus value={query}
                onChange={e => setQuery(e.target.value)}
                placeholder="Имя или сообщение"
                style={{
                  flex: 1, border: 'none', outline: 'none', background: 'transparent',
                  color: T.text, fontSize: 15,
                }}
              />
              {query && (
                <button onClick={() => setQuery('')} style={{
                  width: 18, height: 18, borderRadius: '50%',
                  background: T.textFaint, color: T.bg, border: 'none',
                  display: 'flex', alignItems: 'center', justifyContent: 'center',
                  cursor: 'pointer', padding: 0,
                }}>
                  <svg width="8" height="8" viewBox="0 0 8 8" fill="none">
                    <path d="M1 1L7 7M7 1L1 7" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
                  </svg>
                </button>
              )}
            </div>
            <button onClick={() => { setSearchActive(false); setQuery(''); }} style={{
              border: 'none', background: 'transparent',
              color: T.accent, fontSize: 15, fontWeight: 600, cursor: 'pointer', padding: '0 4px',
            }}>
              Отмена
            </button>
          </div>
        )}
      </div>

      {/* List */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '4px 8px 24px' }}>
        {filtered.length === 0 && query ? (
          <div style={{ padding: '40px 20px', textAlign: 'center', color: T.textMuted, fontSize: 14 }}>
            Ничего не нашлось по «{query}»
          </div>
        ) : (
          filtered.map(chat => (
            <ChatRow key={chat.chat_id} chat={chat} theme={T} onClick={() => onOpenChat(chat)} />
          ))
        )}
      </div>
    </div>
  );
}

function ChatRow({ chat, theme: T, onClick }) {
  const [pressed, setPressed] = useState(false);
  const ts = chat.last_message_time ? new Date(chat.last_message_time).getTime() : null;

  return (
    <div
      onClick={onClick}
      onMouseDown={() => setPressed(true)} onMouseUp={() => setPressed(false)} onMouseLeave={() => setPressed(false)}
      onTouchStart={() => setPressed(true)} onTouchEnd={() => setPressed(false)}
      style={{
        display: 'flex', alignItems: 'center', gap: 13,
        padding: '12px 12px', margin: '0 4px',
        borderRadius: T.radius + 4, cursor: 'pointer',
        background: pressed ? T.surface : 'transparent',
        transition: 'background 0.12s',
      }}
    >
      <Avatar name={chat.chat_name} id={chat.chat_id} size={48} dark={T.isDark} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: 8, marginBottom: 2 }}>
          <span style={{
            fontSize: 16, fontWeight: 600, color: T.text,
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
            letterSpacing: '-0.005em',
          }}>
            {chat.chat_name}
          </span>
          {ts && (
            <span style={{ fontSize: 12, color: T.textFaint, flexShrink: 0, fontVariantNumeric: 'tabular-nums' }}>
              {formatChatTime(ts)}
            </span>
          )}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{
            flex: 1, fontSize: 14, color: T.textMuted, lineHeight: 1.35,
            overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
          }}>
            {chat.last_message_text || ''}
          </div>
          {chat.unread_count > 0 && (
            <div style={{
              minWidth: 22, height: 22, padding: '0 7px',
              borderRadius: 11, background: T.accent, color: T.bubbleOutText,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: 12, fontWeight: 700, fontVariantNumeric: 'tabular-nums',
            }}>
              {chat.unread_count}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ── Message grouping ───────────────────────────────────────────────────────────
function groupMessages(msgs) {
  const groups = [];
  let lastDay = null, lastSender = null, lastTs = null;
  for (const m of msgs) {
    const day = new Date(m.timestamp).toDateString();
    const senderKey = m.is_outgoing ? '__me__' : String(m.sender_id);
    const breakGroup = day !== lastDay || senderKey !== lastSender
      || (lastTs !== null && m.timestamp - lastTs > 5 * 60 * 1000);
    if (day !== lastDay) {
      groups.push({ kind: 'day', ts: m.timestamp });
      lastDay = day;
    }
    const last = groups[groups.length - 1];
    if (breakGroup || !last || last.kind !== 'msgs' || last.sender !== senderKey) {
      groups.push({ kind: 'msgs', sender: senderKey, is_outgoing: m.is_outgoing, items: [] });
    }
    groups[groups.length - 1].items.push(m);
    lastSender = senderKey;
    lastTs = m.timestamp;
  }
  return groups;
}

// ── Typing indicator ───────────────────────────────────────────────────────────
function TypingIndicator({ theme: T }) {
  return (
    <div style={{ display: 'flex', alignItems: 'flex-start' }}>
      <div style={{
        padding: '10px 14px', borderRadius: T.radius + 4, background: T.bubbleIn,
        display: 'flex', alignItems: 'center', gap: 4,
      }}>
        {[0, 1, 2].map(i => (
          <div key={i} style={{
            width: 6, height: 6, borderRadius: '50%', background: T.textMuted, opacity: 0.7,
            animation: `mxd-bounce 1.2s ease-in-out ${i * 0.15}s infinite`,
          }} />
        ))}
      </div>
    </div>
  );
}

// ── Message group ──────────────────────────────────────────────────────────────
function MessageGroup({ group, theme: T }) {
  const isOwn = group.is_outgoing;
  const r = T.radius + 4, tight = 6;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: isOwn ? 'flex-end' : 'flex-start', gap: 2 }}>
      {group.items.map((m, i) => {
        const first = i === 0, last = i === group.items.length - 1;
        const radius = isOwn
          ? `${r}px ${first ? r : tight}px ${last ? tight : tight}px ${r}px`
          : `${first ? r : tight}px ${r}px ${r}px ${last ? tight : tight}px`;
        return (
          <div key={m.message_id} style={{
            maxWidth: '78%', padding: '8px 12px 6px', borderRadius: radius,
            background: isOwn ? T.bubbleOut : T.bubbleIn,
            color: isOwn ? T.bubbleOutText : T.text,
            fontSize: 15, lineHeight: 1.35, wordBreak: 'break-word',
            boxShadow: isOwn ? `0 1px 6px -3px ${T.accentDeep}` : 'none',
          }}>
            <div>{m.text}</div>
            <div style={{
              fontSize: 10.5, marginTop: 2,
              color: isOwn ? 'oklch(0.99 0.005 70 / 0.7)' : T.textFaint,
              textAlign: 'right', display: 'flex', justifyContent: 'flex-end', alignItems: 'center', gap: 3,
              fontVariantNumeric: 'tabular-nums',
            }}>
              {formatMsgTime(m.timestamp)}
              {isOwn && (
                <svg width="16" height="9" viewBox="0 0 16 9" fill="none">
                  <path d="M1 4.5L3.5 7L8 1.5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/>
                  <path d="M7 7L12.5 1.5" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round"/>
                </svg>
              )}
            </div>
          </div>
        );
      })}
    </div>
  );
}

// ── Conversation screen ────────────────────────────────────────────────────────
function ConversationScreen({ theme: T, chat, messages, onBack, onSend }) {
  const [text, setText] = useState('');
  const endRef = useRef(null);

  useEffect(() => {
    endRef.current?.scrollIntoView({ block: 'end' });
  }, [messages.length]);

  const handleSend = () => {
    const t = text.trim();
    if (!t) return;
    onSend(t);
    setText('');
  };

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

  const groups = groupMessages(messages);

  return (
    <div style={{ height: '100%', display: 'flex', flexDirection: 'column', background: T.bg, color: T.text }}>
      {/* Header */}
      <div style={{
        padding: '12px 12px 10px', display: 'flex', alignItems: 'center', gap: 8,
        background: T.bg, borderBottom: `0.5px solid ${T.stroke}`,
      }}>
        <button onClick={onBack} aria-label="Назад" style={{
          width: 36, height: 36, borderRadius: 18,
          background: 'transparent', border: 'none', color: T.accent,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
          cursor: 'pointer', flexShrink: 0,
        }}>
          <svg width="11" height="18" viewBox="0 0 11 18" fill="none">
            <path d="M9.5 1L1.5 9L9.5 17" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </button>
        <Avatar name={chat.chat_name} id={chat.chat_id} size={36} dark={T.isDark} />
        <div style={{ flex: 1, minWidth: 0, marginLeft: 4 }}>
          <div style={{ fontSize: 15, fontWeight: 600, color: T.text, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
            {chat.chat_name}
          </div>
          <div style={{ fontSize: 12, color: T.textMuted, marginTop: 1 }}>
            был(а) недавно
          </div>
        </div>
      </div>

      {/* Messages */}
      <div style={{
        flex: 1, overflowY: 'auto', padding: '12px 12px 8px',
        display: 'flex', flexDirection: 'column', gap: 6,
      }}>
        {groups.map((g, i) => g.kind === 'day' ? (
          <div key={`d${i}`} style={{
            alignSelf: 'center', margin: '12px 0 6px', padding: '4px 12px',
            borderRadius: 999, background: T.surface, color: T.textMuted,
            fontSize: 11, fontWeight: 600, letterSpacing: '0.02em', textTransform: 'uppercase',
          }}>
            {formatDayHeader(g.ts)}
          </div>
        ) : (
          <MessageGroup key={`g${i}`} group={g} theme={T} />
        ))}
        <div ref={endRef} />
      </div>

      {/* Input */}
      <div style={{
        padding: '8px 10px 12px', background: T.bg,
        borderTop: `0.5px solid ${T.stroke}`,
        display: 'flex', alignItems: 'flex-end', gap: 8,
      }}>
        <div style={{
          flex: 1, display: 'flex', alignItems: 'flex-end',
          background: T.surface, borderRadius: 19,
          padding: '0 4px 0 14px', minHeight: 38,
        }}>
          <textarea
            value={text} onChange={e => setText(e.target.value)} onKeyDown={handleKey}
            placeholder="Сообщение" rows={1}
            style={{
              flex: 1, border: 'none', outline: 'none', background: 'transparent',
              color: T.text, fontSize: 15, lineHeight: '20px',
              resize: 'none', maxHeight: 100, padding: '9px 0', minWidth: 0,
            }}
          />
          <button aria-label="Эмодзи" style={{
            width: 30, height: 30, borderRadius: 15, flexShrink: 0,
            background: 'transparent', border: 'none', color: T.textFaint,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            cursor: 'pointer', alignSelf: 'flex-end', marginBottom: 4, padding: 0,
          }}>
            <svg width="20" height="20" viewBox="0 0 20 20" fill="none">
              <circle cx="10" cy="10" r="8" stroke="currentColor" strokeWidth="1.6"/>
              <circle cx="7" cy="8.5" r="1" fill="currentColor"/>
              <circle cx="13" cy="8.5" r="1" fill="currentColor"/>
              <path d="M6.5 12.5C7.5 13.8 8.7 14.5 10 14.5C11.3 14.5 12.5 13.8 13.5 12.5"
                    stroke="currentColor" strokeWidth="1.6" strokeLinecap="round"/>
            </svg>
          </button>
        </div>
        <button
          onClick={handleSend} disabled={!text.trim()} aria-label="Отправить"
          style={{
            width: 38, height: 38, borderRadius: 19, flexShrink: 0,
            background: text.trim() ? T.accent : T.surface,
            border: 'none', color: text.trim() ? T.bubbleOutText : T.textFaint,
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            cursor: text.trim() ? 'pointer' : 'default',
            transition: 'background 0.15s, color 0.15s',
            boxShadow: text.trim() ? `0 4px 12px -4px ${T.accent}` : 'none',
          }}
        >
          <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
            <path d="M8 13V3M8 3L3 8M8 3L13 8" stroke="currentColor"
                  strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round"/>
          </svg>
        </button>
      </div>
    </div>
  );
}

// ── App ────────────────────────────────────────────────────────────────────────
function App() {
  const isDark = (tg.colorScheme || 'dark') !== 'light';
  const T = makeTheme(isDark);

  const [stage, setStage] = useState('loading'); // loading | error | empty | list | chat
  const [chats, setChats] = useState([]);
  const [activeChatId, setActiveChatId] = useState(null);
  const [messages, setMessages] = useState([]);
  const [errorMsg, setErrorMsg] = useState('');

  const activeChat = chats.find(c => String(c.chat_id) === String(activeChatId));

  // Sync body background
  useEffect(() => { document.body.style.background = T.bg; }, [T.bg]);

  // Telegram back button
  useEffect(() => {
    if (!tg.BackButton) return;
    if (stage === 'chat') {
      tg.BackButton.show();
      tg.BackButton.onClick(handleBack);
      return () => { tg.BackButton.offClick(handleBack); tg.BackButton.hide(); };
    } else {
      tg.BackButton.hide();
    }
  }, [stage]);

  const loadChats = useCallback(async () => {
    setStage('loading');
    setErrorMsg('');
    try {
      const data = await apiRequest('/chats');
      const list = Array.isArray(data) ? data : (data.chats || []);
      setChats(list);
      setStage(list.length === 0 ? 'empty' : 'list');
    } catch (err) {
      console.error('loadChats:', err);
      setErrorMsg('Не удалось загрузить чаты. Попробуйте ещё раз.');
      setStage('error');
    }
  }, []);

  useEffect(() => {
    if (tg.initData || IS_DEV) {
      loadChats();
    } else {
      setErrorMsg('Приложение должно быть открыто из Telegram');
      setStage('error');
    }
  }, [loadChats]);

  const handleOpenChat = async (chat) => {
    setActiveChatId(chat.chat_id);
    setMessages([]);
    setStage('chat');
    // mark as read locally
    setChats(prev => prev.map(c =>
      String(c.chat_id) === String(chat.chat_id) ? { ...c, unread_count: 0 } : c
    ));
    try {
      const data = await apiRequest('/messages/history', {
        method: 'POST',
        body: JSON.stringify({ chat_id: chat.chat_id, limit: 50 }),
      });
      setMessages(data.messages || []);
    } catch (err) {
      console.error('loadMessages:', err);
    }
  };

  function handleBack() {
    setStage('list');
    setActiveChatId(null);
    setMessages([]);
  }

  const handleSend = async (text) => {
    if (!activeChatId) return;
    // Optimistic message
    const tmp = {
      message_id: 'tmp_' + Date.now(),
      chat_id: activeChatId,
      sender_id: 0, sender_name: '',
      text, timestamp: Date.now(), is_outgoing: true,
    };
    setMessages(prev => [...prev, tmp]);
    setChats(prev => prev.map(c =>
      String(c.chat_id) === String(activeChatId)
        ? { ...c, last_message_text: text, last_message_time: new Date().toISOString() }
        : c
    ));
    try {
      await apiRequest('/messages/send', {
        method: 'POST',
        body: JSON.stringify({ chat_id: activeChatId, text }),
      });
    } catch (err) {
      console.error('sendMessage:', err);
    }
  };

  if (stage === 'loading') return <LoadingScreen theme={T} />;
  if (stage === 'error')   return <ErrorScreen theme={T} message={errorMsg} onRetry={loadChats} />;
  if (stage === 'empty')   return <EmptyScreen theme={T} />;
  if (stage === 'chat' && activeChat)
    return <ConversationScreen theme={T} chat={activeChat} messages={messages} onBack={handleBack} onSend={handleSend} />;
  return <ChatList theme={T} chats={chats} onOpenChat={handleOpenChat} />;
}

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