const { useState: useStateC, useEffect: useEffectC, useRef: useRefC, useMemo: useMemoC } = React;

// ---- Per-user session persistence ----
function sessionsKey(userId) { return 'fnre-sessions-' + (userId || 'anon'); }
function loadSessions(userId) {
  try { return JSON.parse(localStorage.getItem(sessionsKey(userId))) || []; } catch (e) { return []; }
}
function saveSessions(userId, s) {
  try { localStorage.setItem(sessionsKey(userId), JSON.stringify(s)); } catch (e) {}
}

// Generate a short session ID like "s-4f8a2c1e" (used as phone field — user never sees it)
function newSessionId() {
  const bytes = new Uint8Array(6);
  (crypto || window.crypto).getRandomValues(bytes);
  return 's-' + Array.from(bytes).map(b => b.toString(16).padStart(2,'0')).join('');
}

function fmtTime(d) {
  const x = d instanceof Date ? d : new Date(d);
  return x.getHours().toString().padStart(2, '0') + ':' + x.getMinutes().toString().padStart(2, '0');
}

function fmtRelative(iso) {
  if (!iso) return '';
  const diffMs = Date.now() - new Date(iso).getTime();
  const mins = Math.floor(diffMs / 60000);
  if (mins < 1) return 'now';
  if (mins < 60) return mins + 'm';
  const hrs = Math.floor(mins / 60);
  if (hrs < 24) return hrs + 'h';
  const days = Math.floor(hrs / 24);
  return days + 'd';
}

// Transform a DB row from re_conversation_analysis into the shape the panel expects
function analysisRowToProps(row) {
  if (!row) return null;
  return {
    intent: row.intent || '—',
    budget: row.budget || '—',
    area: row.area || '—',
    type: row.preferred_type ? row.preferred_type.toUpperCase() : '—',
    sentiment: row.sentiment || 'Neutral',
    priority: row.priority || 'Warm',
    leadScore: row.lead_score || 3,
    summary: row.summary || '',
    keyDetails: Array.isArray(row.key_details) ? row.key_details : [],
    nextAction: row.next_action || '',
    risks: row.risks || null,
    tags: Array.isArray(row.tags) ? row.tags : [],
    analyzedAt: row.analyzed_at
  };
}

// ---- AI Analysis Panel (right rail) ----
const AnalysisPanel = ({ analysis, session, isMock }) => {
  const [liveAnalysis, setLiveAnalysis] = useStateC(null);
  const [liveLoading, setLiveLoading] = useStateC(false);

  // For REAL (non-mock) chats: fetch from re_conversation_analysis + poll every 30s
  useEffectC(() => {
    if (isMock || !session?.phone) { setLiveAnalysis(null); return; }
    let cancelled = false;

    const fetch = async () => {
      try {
        const rows = await window.__api.sbGet('re_conversation_analysis', {
          'select': '*',
          'phone_number': 'eq.' + session.phone,
          'order': 'analyzed_at.desc',
          'limit': '1'
        });
        if (cancelled) return;
        setLiveAnalysis(rows && rows.length > 0 ? analysisRowToProps(rows[0]) : null);
      } catch (e) {
        if (!cancelled) console.warn('[analysis fetch]', e.message);
      } finally {
        if (!cancelled) setLiveLoading(false);
      }
    };
    setLiveLoading(true);
    fetch();
    const id = setInterval(fetch, 30000);
    return () => { cancelled = true; clearInterval(id); };
  }, [session?.phone, isMock]);

  // Mock chats use the passed-in analysis; real chats use the live one
  const active = isMock ? analysis : liveAnalysis;

  if (!active && !session) {
    return (
      <div className="chat-analysis">
        <div style={{ padding: 22, textAlign: 'center', color: 'var(--fg-4)', fontSize: 12 }}>
          {window.__t('analysis.selectConvo')}
        </div>
      </div>
    );
  }

  if (!active) {
    return (
      <div className="chat-analysis">
        <div style={{ padding: 22 }}>
          <div className="mono" style={{ fontSize: 10, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 8 }}>{window.__t('analysis.pending.title')}</div>
          <h3 style={{ margin: '0 0 6px', fontFamily: 'var(--serif, var(--sans))', fontWeight: 500, fontSize: 16 }}>
            {(session?.name || window.__t('chat.newLead')).split(' ')[0]}
          </h3>
          {liveLoading ? (
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginTop: 4 }}>
              <div className="spinner sm" />
            </div>
          ) : (
            <p style={{ fontSize: 12, color: 'var(--fg-3)', lineHeight: 1.5, margin: 0 }}>
              {window.__t('analysis.pending.body')}
            </p>
          )}
        </div>
      </div>
    );
  }
  // From here on, `active` replaces the old `analysis` variable
  const analysisData = active;

  const priorityColor = analysisData.priority === 'Hot' ? '#ef4444' :
                        analysisData.priority === 'Warm' ? '#f59e0b' :
                        analysisData.priority === 'Cold — needs re-engagement' ? '#3b82f6' : 'var(--fg-3)';
  const sentimentColor = /negative|cold/i.test(analysisData.sentiment) ? '#ef4444' :
                         /positive|decisive|analytical|pragmatic|relieved/i.test(analysisData.sentiment) ? '#10b981' :
                         /neutral/i.test(analysisData.sentiment) ? '#6b7280' : 'var(--fg-3)';

  return (
    <div className="chat-analysis">
      <div style={{ padding: '20px 22px 0' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
          <div className="mono" style={{ fontSize: 10, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.14em' }}>{window.__t('analysis.title')}</div>
          {isMock && <span style={{ fontSize: 9, fontWeight: 600, padding: '2px 8px', borderRadius: 999, background: 'rgba(245,158,11,0.12)', color: '#b45309', letterSpacing: '0.06em' }}>DEMO</span>}
        </div>
        <h3 style={{ margin: '4px 0 14px', fontFamily: 'var(--serif, var(--sans))', fontWeight: 500, fontSize: 18, lineHeight: 1.2 }}>
          {(session?.name || 'Lead').split(' ')[0]}
        </h3>

        {/* Priority + Score */}
        <div style={{ display: 'flex', gap: 8, marginBottom: 14, flexWrap: 'wrap' }}>
          <span style={{ fontSize: 10, fontWeight: 700, padding: '3px 10px', borderRadius: 999, background: priorityColor, color: 'white', textTransform: 'uppercase', letterSpacing: '0.06em' }}>
            {analysisData.priority}
          </span>
          <span style={{ fontSize: 10, fontWeight: 600, padding: '3px 10px', borderRadius: 999, background: 'var(--bg-soft)', color: 'var(--fg-2)', display: 'inline-flex', gap: 4, alignItems: 'center' }}>
            {window.__t('analysis.score')} {analysisData.leadScore}/5
          </span>
          <span style={{ fontSize: 10, fontWeight: 500, padding: '3px 10px', borderRadius: 999, background: 'var(--bg-soft)', color: sentimentColor }}>
            {analysisData.sentiment}
          </span>
        </div>
      </div>

      <div style={{ padding: '0 22px', overflowY: 'auto', flex: 1, paddingBottom: 22 }}>
        {/* Summary */}
        <div style={{ marginBottom: 18 }}>
          <div className="mono" style={{ fontSize: 9, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 6 }}>{window.__t('analysis.summary')}</div>
          <p style={{ margin: 0, fontSize: 12.5, lineHeight: 1.55, color: 'var(--fg-1)' }}>{analysisData.summary}</p>
        </div>

        {/* Profile grid */}
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10, marginBottom: 18 }}>
          <div style={{ background: 'var(--bg-soft)', padding: '10px 12px', borderRadius: 8 }}>
            <div className="mono" style={{ fontSize: 9, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 4 }}>{window.__t('analysis.intent')}</div>
            <div style={{ fontSize: 13, color: 'var(--fg)' }}>{analysisData.intent}</div>
          </div>
          <div style={{ background: 'var(--bg-soft)', padding: '10px 12px', borderRadius: 8 }}>
            <div className="mono" style={{ fontSize: 9, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 4 }}>{window.__t('analysis.budget')}</div>
            <div style={{ fontSize: 13, color: 'var(--fg)' }}>{analysisData.budget}</div>
          </div>
          <div style={{ background: 'var(--bg-soft)', padding: '10px 12px', borderRadius: 8 }}>
            <div className="mono" style={{ fontSize: 9, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 4 }}>{window.__t('analysis.area')}</div>
            <div style={{ fontSize: 13, color: 'var(--fg)' }}>{analysisData.area}</div>
          </div>
          <div style={{ background: 'var(--bg-soft)', padding: '10px 12px', borderRadius: 8 }}>
            <div className="mono" style={{ fontSize: 9, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 4 }}>{window.__t('analysis.type')}</div>
            <div style={{ fontSize: 13, color: 'var(--fg)' }}>{analysisData.type}</div>
          </div>
        </div>

        {/* Key details */}
        {analysisData.keyDetails && analysisData.keyDetails.length > 0 && (
          <div style={{ marginBottom: 18 }}>
            <div className="mono" style={{ fontSize: 9, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 8 }}>{window.__t('analysis.keyDetails')}</div>
            <ul style={{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 6 }}>
              {analysisData.keyDetails.map((d, i) => (
                <li key={i} style={{ display: 'flex', gap: 8, fontSize: 12.5, color: 'var(--fg-1)', lineHeight: 1.5 }}>
                  <span style={{ color: 'var(--fg-3)', flexShrink: 0, marginTop: 6 }}>
                    <svg width="4" height="4" viewBox="0 0 4 4"><circle cx="2" cy="2" r="2" fill="currentColor"/></svg>
                  </span>
                  {d}
                </li>
              ))}
            </ul>
          </div>
        )}

        {/* Next action */}
        <div style={{ background: 'rgba(16,185,129,0.08)', border: '1px solid rgba(16,185,129,0.2)', padding: '12px 14px', borderRadius: 8, marginBottom: 14 }}>
          <div className="mono" style={{ fontSize: 9, color: '#059669', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 6, display: 'flex', gap: 6, alignItems: 'center' }}>
            <Icon name="lightbulb" size={10} /> {window.__t('analysis.nextAction')}
          </div>
          <p style={{ margin: 0, fontSize: 12.5, lineHeight: 1.55, color: 'var(--fg-1)' }}>{analysisData.nextAction}</p>
        </div>

        {/* Risks */}
        {analysisData.risks && (
          <div style={{ background: 'rgba(239,68,68,0.06)', border: '1px solid rgba(239,68,68,0.18)', padding: '12px 14px', borderRadius: 8, marginBottom: 14 }}>
            <div className="mono" style={{ fontSize: 9, color: '#b91c1c', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 6, display: 'flex', gap: 6, alignItems: 'center' }}>
              <Icon name="alertTriangle" size={10} /> {window.__t('analysis.risks')}
            </div>
            <p style={{ margin: 0, fontSize: 12.5, lineHeight: 1.55, color: 'var(--fg-1)' }}>{analysisData.risks}</p>
          </div>
        )}

        {/* Tags */}
        {analysisData.tags && (
          <div>
            <div className="mono" style={{ fontSize: 9, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 6 }}>{window.__t('analysis.tags')}</div>
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 5 }}>
              {analysisData.tags.map((tag, i) => (
                <span key={i} className="mono" style={{ fontSize: 10, padding: '3px 8px', borderRadius: 999, background: 'var(--bg-soft)', color: 'var(--fg-3)' }}>
                  {tag}
                </span>
              ))}
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

// ---- Session List (sidebar) with demo + live sections ----
const SessionList = ({ sessions, mockChats, activeId, onPick, onNew }) => {
  return (
    <div className="chat-side">
      <div className="chat-side-head">
        <h3>{window.__t('chat.conversations')}</h3>
        <div className="sub">{sessions.length} {window.__t('chat.live')} · {mockChats.length} {window.__t('chat.demo')}</div>
      </div>
      <button data-onboarding-id="chat-new-btn" className="new-thread-btn" onClick={onNew}><Icon name="plus" size={13} /> {window.__t('chat.newConversation')}</button>

      <div className="thread-list">
        {sessions.length > 0 && (
          <>
            <div className="nav-label" style={{ padding: '8px 10px 4px' }}>{window.__t('chat.yours')}</div>
            {sessions.map(s => (
              <div key={s.phone} className={`thread ${activeId === s.phone ? 'active' : ''}`} onClick={() => onPick(s.phone)}>
                <div className="t-top">
                  <div className="t-title">{(s.name || window.__t('chat.newLead')).split(' ')[0]}</div>
                  <div className="t-time">{fmtRelative(s.lastActivity)}</div>
                </div>
                <div className="t-prev">{s.lastMessagePreview || window.__t('chat.waiting')}</div>
              </div>
            ))}
          </>
        )}

        <div className="nav-label" style={{ padding: '12px 10px 4px' }}>{window.__t('chat.demoScenarios')}</div>
        {mockChats.map(m => (
          <div key={m.id} className={`thread ${activeId === m.id ? 'active' : ''}`} onClick={() => onPick(m.id)}>
            <div className="t-top">
              <div className="t-title" style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                {m.name.split(' ')[0]}
                <span style={{ fontSize: 8, fontWeight: 600, padding: '1px 5px', borderRadius: 3, background: 'rgba(245,158,11,0.16)', color: '#b45309', letterSpacing: '0.04em' }}>DEMO</span>
              </div>
              <div className="t-time">{fmtRelative(m.lastActivity)}</div>
            </div>
            <div className="t-prev" style={{ fontStyle: 'italic', fontSize: 11.5, color: 'var(--fg-4)' }}>{m.scenario}</div>
            <div className="t-prev" style={{ marginTop: 2 }}>{m.lastMessagePreview}</div>
          </div>
        ))}
      </div>
    </div>
  );
};

// ---- Message text with inline property photos ----
// The agent shares property photos either as a "Photo: <url>" line or as a
// bare image URL. We pull those out of the text and render them as images
// so the customer sees the actual photo (with the price/location text intact).
const IMG_URL_RE = /(?:photos?\s*:\s*)?(https?:\/\/[^\s]+?\.(?:jpe?g|png|webp|gif)(?:\?[^\s]*)?|https?:\/\/images\.unsplash\.com\/[^\s]+)/gi;

const extractPhotos = (text) => {
  const urls = [];
  if (!text) return { clean: '', urls };
  const clean = text.replace(IMG_URL_RE, (m, url) => {
    if (url && !urls.includes(url)) urls.push(url);
    return '';
  })
  // tidy up empty "Photo:" labels and the blank lines they leave behind
  .replace(/^[ \t]*photos?\s*:?[ \t]*$/gim, '')
  .replace(/\n{3,}/g, '\n\n')
  .trim();
  return { clean, urls };
};

const PropertyPhotos = ({ urls }) => {
  if (!urls || urls.length === 0) return null;
  return (
    <div className="m-photos" style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 6 }}>
      {urls.map((u, i) => (
        <a key={i} href={u} target="_blank" rel="noopener noreferrer" style={{ display: 'block', flex: urls.length === 1 ? '1 1 100%' : '1 1 46%' }}>
          <img
            src={u}
            alt="Property photo"
            loading="lazy"
            style={{ width: '100%', maxHeight: 220, objectFit: 'cover', borderRadius: 10, border: '1px solid var(--line, rgba(0,0,0,0.08))', display: 'block' }}
          />
        </a>
      ))}
    </div>
  );
};

const MessageText = ({ text }) => {
  const { clean, urls } = extractPhotos(text);
  return (
    <>
      {clean && <div className="m-text" style={{ whiteSpace: 'pre-wrap' }}>{clean}</div>}
      <PropertyPhotos urls={urls} />
    </>
  );
};

// ---- Voice message bubble (audio player + collapsible transcription) ----
const VoiceBubble = ({ audioUrl, audioPath, transcription, pending, error }) => {
  const [resolvedUrl, setResolvedUrl] = useStateC(audioUrl || null);
  const [showTx, setShowTx] = useStateC(false);

  useEffectC(() => {
    let cancelled = false;
    if (audioUrl) { setResolvedUrl(audioUrl); return; }
    if (!audioPath) { setResolvedUrl(null); return; }
    window.__api.getVoiceSignedUrl(audioPath).then(u => {
      if (!cancelled) setResolvedUrl(u);
    });
    return () => { cancelled = true; };
  }, [audioUrl, audioPath]);

  const txReady = !pending && transcription && !error;
  const txLabel = error
    ? (transcription || 'Voice failed')
    : pending
      ? 'Transcribing…'
      : (showTx ? 'Hide transcription' : 'View transcription');

  return (
    <div className="m-voice">
      <div className="voice-player">
        {resolvedUrl ? (
          <audio controls preload="metadata" src={resolvedUrl} />
        ) : (
          <div className="voice-loading"><span className="spinner sm" /></div>
        )}
      </div>
      <button
        type="button"
        className={`voice-tx-toggle ${error ? 'is-error' : ''}`}
        onClick={() => txReady && setShowTx(v => !v)}
        disabled={!txReady}
      >
        {txLabel}
      </button>
      {showTx && txReady && (
        <div className="voice-tx-text">{transcription}</div>
      )}
    </div>
  );
};

// ---- Chat Panel ----
const ChatPanel = ({ session, onBumpActivity }) => {
  const [messages, setMessages] = useStateC(session._preloadedMessages || []);
  const [input, setInput] = useStateC('');
  const [isTyping, setIsTyping] = useStateC(false);
  const [loading, setLoading] = useStateC(!session._preloadedMessages);
  const [recordState, setRecordState] = useStateC('idle'); // 'idle' | 'recording' | 'sending'
  const [recordSeconds, setRecordSeconds] = useStateC(0);
  const bodyRef = useRefC(null);
  const bottomRef = useRefC(null);
  const typingTimeoutRef = useRefC(null);
  const mediaRecorderRef = useRefC(null);
  const audioStreamRef = useRefC(null);
  const audioChunksRef = useRefC([]);
  const recordTimerRef = useRefC(null);
  const blobUrlsRef = useRefC([]);

  const isMock = !!session.isMock;
  const MAX_RECORD_SECONDS = 120;

  // Load history + sync the lead name from DB (real chats only — mocks are preloaded)
  useEffectC(() => {
    if (isMock) {
      setMessages(session._preloadedMessages || []);
      setLoading(false);
      return;
    }
    let cancelled = false;
    setLoading(true);
    setMessages([]);
    const loadAll = async () => {
      try {
        const [logs, leadRows] = await Promise.all([
          window.__api.loadChatHistory(session.phone, 50),
          window.__api.sbGet('re_leads', {
            'select': 'name,contact_phone,language',
            'phone': 'eq.' + session.phone,
            'limit': '1'
          }).catch(() => [])
        ]);
        if (cancelled) return;
        const msgs = (logs || [])
          .filter(l => l.message_content && l.message_content.trim())
          .map(l => {
            const isVoice = l.sender_type === 'customer' && l.message_type === 'voice';
            if (isVoice) {
              return {
                _id: l.id,
                role: 'user',
                kind: 'voice',
                message_id: l.message_id,
                audioPath: l.audio_path || null,
                transcription: l.message_content,
                pending: false,
                time: fmtTime(l.created_at)
              };
            }
            return {
              _id: l.id,
              role: l.sender_type === 'customer' ? 'user' : 'ai',
              text: l.message_content,
              time: fmtTime(l.created_at)
            };
          });
        setMessages(prev => {
          const historyIds = new Set(msgs.map(m => m._id).filter(Boolean));
          const extras = prev.filter(p => p._id && !historyIds.has(p._id));
          return [...msgs, ...extras];
        });
        // If the DB has a real name (or contact_phone) for this lead, bubble it up
        const lead = leadRows?.[0];
        if (lead && (lead.name && lead.name !== 'New Lead' || lead.contact_phone)) {
          if (typeof session._onSync === 'function') {
            session._onSync({ name: lead.name && lead.name !== 'New Lead' ? lead.name : session.name, language: lead.language || session.language });
          }
        }
      } catch (e) {
        console.warn('history load failed:', e);
      } finally {
        if (!cancelled) setLoading(false);
      }
    };
    loadAll();
    return () => { cancelled = true; };
  }, [session.phone, isMock]);

  // Subscribe to incoming AI replies for this session via Supabase Realtime
  useEffectC(() => {
    if (isMock) return;
    const unsubscribe = window.__api.subscribeToChat(session.phone, (row) => {
      // Customer voice transcript landing — match optimistic bubble by message_id and update it.
      if (row.sender_type === 'customer' && row.message_type === 'voice') {
        setMessages(m => {
          const idx = m.findIndex(x => x.kind === 'voice' && x.message_id === row.message_id);
          if (idx >= 0) {
            const updated = [...m];
            updated[idx] = {
              ...updated[idx],
              _id: row.id,
              transcription: row.message_content,
              audioPath: row.audio_path || updated[idx].audioPath,
              pending: false
            };
            return updated;
          }
          // No optimistic match (e.g. page reloaded mid-transcription) — append fresh.
          if (m.some(x => x._id === row.id)) return m;
          return [...m, {
            _id: row.id,
            role: 'user',
            kind: 'voice',
            message_id: row.message_id,
            audioPath: row.audio_path || null,
            transcription: row.message_content,
            pending: false,
            time: fmtTime(row.created_at)
          }];
        });
        return;
      }

      // AI replies — existing behavior.
      if (row.sender_type !== 'ai_agent') return;
      setMessages(m => {
        if (m.some(x => x._id === row.id)) return m;
        return [...m, {
          _id: row.id,
          role: 'ai',
          text: row.message_content,
          time: fmtTime(row.created_at)
        }];
      });
      if (typingTimeoutRef.current) {
        clearTimeout(typingTimeoutRef.current);
        typingTimeoutRef.current = null;
      }
      setIsTyping(false);
      onBumpActivity(session.phone, row.message_content);
    });
    return () => {
      unsubscribe();
      if (typingTimeoutRef.current) {
        clearTimeout(typingTimeoutRef.current);
        typingTimeoutRef.current = null;
      }
    };
  }, [session.phone, isMock]);

  useEffectC(() => {
    requestAnimationFrame(() => {
      requestAnimationFrame(() => {
        if (bottomRef.current) bottomRef.current.scrollIntoView({ behavior: 'smooth', block: 'end' });
        else if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight;
      });
    });
  }, [messages, isTyping]);

  // ---- Voice recording ----
  const fmtRecordTime = (s) => {
    const mm = Math.floor(s / 60).toString().padStart(2, '0');
    const ss = (s % 60).toString().padStart(2, '0');
    return mm + ':' + ss;
  };

  const cleanupRecorder = () => {
    if (recordTimerRef.current) { clearInterval(recordTimerRef.current); recordTimerRef.current = null; }
    if (audioStreamRef.current) {
      audioStreamRef.current.getTracks().forEach(t => t.stop());
      audioStreamRef.current = null;
    }
    mediaRecorderRef.current = null;
    audioChunksRef.current = [];
  };

  // Cleanup on unmount: stop mic + revoke any blob URLs we created
  useEffectC(() => {
    return () => {
      cleanupRecorder();
      blobUrlsRef.current.forEach(u => { try { URL.revokeObjectURL(u); } catch (e) {} });
      blobUrlsRef.current = [];
    };
  }, []);

  const sendVoice = async (blob, mimetype) => {
    const messageId = 'WEB-VOICE-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8);
    const localUrl = URL.createObjectURL(blob);
    blobUrlsRef.current.push(localUrl);
    const now = new Date();

    setMessages(m => [...m, {
      role: 'user',
      kind: 'voice',
      message_id: messageId,
      audioUrl: localUrl,
      audioPath: null,
      transcription: null,
      pending: true,
      time: fmtTime(now)
    }]);
    setIsTyping(true);
    onBumpActivity(session.phone, '🎤 Voice message');

    typingTimeoutRef.current = setTimeout(() => {
      setMessages(m => [...m, { role: 'ai', text: "I'm taking longer than usual. Please try again in a moment.", time: fmtTime(new Date()) }]);
      setIsTyping(false);
      typingTimeoutRef.current = null;
    }, 120000);

    try {
      const { path, signedUrl } = await window.__api.uploadVoiceNote(blob, mimetype, messageId);
      setMessages(m => m.map(x => x.message_id === messageId ? { ...x, audioPath: path } : x));
      await window.__api.postVoiceToAgent({
        phone: session.phone,
        name: session.name || 'Unknown',
        message_id: messageId,
        language: session.language || 'en',
        audio_url: signedUrl,
        audio_mimetype: mimetype,
        audio_path: path
      });
    } catch (e) {
      console.error('voice send failed', e);
      if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current); typingTimeoutRef.current = null; }
      setIsTyping(false);
      setMessages(m => m.map(x => x.message_id === messageId
        ? { ...x, pending: false, transcription: 'Voice failed to upload', error: true }
        : x));
    }
  };

  const startRecording = async () => {
    if (isMock || isTyping || recordState !== 'idle') return;
    if (!navigator.mediaDevices?.getUserMedia || typeof window.MediaRecorder === 'undefined') {
      setMessages(m => [...m, { role: 'ai', text: 'Voice recording is not supported in this browser.', time: fmtTime(new Date()) }]);
      return;
    }
    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      audioStreamRef.current = stream;
      const candidates = ['audio/webm;codecs=opus', 'audio/webm', 'audio/mp4', 'audio/ogg;codecs=opus'];
      let mimeType = '';
      for (const m of candidates) {
        if (window.MediaRecorder.isTypeSupported && window.MediaRecorder.isTypeSupported(m)) { mimeType = m; break; }
      }
      const rec = mimeType ? new MediaRecorder(stream, { mimeType }) : new MediaRecorder(stream);
      audioChunksRef.current = [];
      rec.ondataavailable = (e) => { if (e.data && e.data.size > 0) audioChunksRef.current.push(e.data); };
      rec.onstop = () => {
        const wasCancelled = rec._cancelled;
        const chunks = audioChunksRef.current.slice();
        const recMime = rec.mimeType || mimeType || 'audio/webm';
        cleanupRecorder();
        if (wasCancelled || chunks.length === 0) {
          setRecordState('idle');
          setRecordSeconds(0);
          return;
        }
        setRecordState('idle');
        setRecordSeconds(0);
        const blob = new Blob(chunks, { type: recMime });
        sendVoice(blob, recMime);
      };
      rec.start();
      mediaRecorderRef.current = rec;
      setRecordState('recording');
      setRecordSeconds(0);
      recordTimerRef.current = setInterval(() => {
        setRecordSeconds(s => {
          const next = s + 1;
          if (next >= MAX_RECORD_SECONDS) { stopRecording(); }
          return next;
        });
      }, 1000);
    } catch (e) {
      console.error('mic permission failed', e);
      setMessages(m => [...m, { role: 'ai', text: 'Microphone permission denied or unavailable.', time: fmtTime(new Date()) }]);
      cleanupRecorder();
      setRecordState('idle');
    }
  };

  const stopRecording = () => {
    const rec = mediaRecorderRef.current;
    if (rec && rec.state !== 'inactive') rec.stop();
  };

  const cancelRecording = () => {
    const rec = mediaRecorderRef.current;
    if (rec) {
      rec._cancelled = true;
      if (rec.state !== 'inactive') rec.stop();
    } else {
      cleanupRecorder();
      setRecordState('idle');
      setRecordSeconds(0);
    }
  };

  const send = async (text) => {
    if (!text.trim() || isTyping || isMock) return;
    const now = new Date();
    const messageId = 'WEB-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8);

    setMessages(m => [...m, { role: 'user', text, time: fmtTime(now) }]);
    setInput('');
    setIsTyping(true);
    onBumpActivity(session.phone, text);

    typingTimeoutRef.current = setTimeout(() => {
      setMessages(m => [...m, { role: 'ai', text: "I'm taking longer than usual. Please try again in a moment.", time: fmtTime(new Date()) }]);
      setIsTyping(false);
      typingTimeoutRef.current = null;
    }, 120000);

    try {
      await window.__api.postToAgent({
        phone: session.phone,
        name: session.name || 'Unknown',
        user_message: text,
        message_id: messageId,
        language: session.language || 'en'
      });
    } catch (e) {
      console.error('post failed:', e);
      if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current); typingTimeoutRef.current = null; }
      setMessages(m => [...m, { role: 'ai', text: 'Network error reaching the assistant. Please try again.', time: fmtTime(new Date()) }]);
      setIsTyping(false);
      return;
    }

    // Fallback to Realtime: poll the DB for the agent's reply in case the
    // Realtime INSERT event never reaches the browser. Deduped by row id.
    (async () => {
      const sinceIso = new Date(now.getTime() - 2000).toISOString();
      const row = await window.__api.pollForReply(session.phone, sinceIso, 115000, 2000);
      if (!row) return;
      setMessages(m => (row.id && m.some(x => x._id === row.id))
        ? m
        : [...m, { _id: row.id, role: 'ai', text: row.message_content, time: fmtTime(row.created_at) }]);
      if (typingTimeoutRef.current) { clearTimeout(typingTimeoutRef.current); typingTimeoutRef.current = null; }
      setIsTyping(false);
    })();

    // Fire the "you're in the database" tutorial once we confirm the
    // lead row exists in re_leads (proves real-time fetch).
    window.__leadTutorial?.pollAndFire(session.phone);
  };

  const onKey = (e) => {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); send(input); }
  };

  return (
    <div className="chat-main">
      <div className="chat-head">
        <div className="title-row">
          <div className="ai-mark">{(session.name || '?').slice(0, 1).toUpperCase()}</div>
          <div>
            <h3 style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
              {(session.name || 'New Lead').split(' ')[0]}
              {isMock && <span style={{ fontSize: 9, fontWeight: 600, padding: '2px 6px', borderRadius: 3, background: 'rgba(245,158,11,0.16)', color: '#b45309', letterSpacing: '0.04em' }}>DEMO</span>}
            </h3>
            <div className="status">
              {isMock ? session.scenario : ((session.language || 'en') === 'ar' ? window.__t('top.lang.ar') : window.__t('top.lang.en')) + ' · ' + window.__t('chat.webChannel')}
            </div>
          </div>
        </div>
        <div style={{ display: 'flex', gap: 8 }}>
          {isMock
            ? <span className="tag" style={{ background: 'rgba(245,158,11,0.1)', color: '#b45309' }}>{window.__t('chat.demoReadonly')}</span>
            : <span className="tag maroon">{isTyping ? window.__t('chat.typing') : window.__t('chat.live.badge')}</span>
          }
        </div>
      </div>

      <div className="chat-body" ref={bodyRef}>
        {loading && (
          <div className="loading-block"><div className="spinner" /></div>
        )}
        {!loading && messages.length === 0 && !isMock && (
          <div style={{ color: 'var(--fg-4)', textAlign: 'center', padding: 40, fontSize: 13 }}>
            {window.__t('chat.sayHi')}
          </div>
        )}
        {messages.map((m, i) => (
          <div key={m._id || m.message_id || i} className={`msg ${m.role}`}>
            <div className="m-avatar">{m.role === 'user' ? (session.name || 'C').slice(0, 1).toUpperCase() : 'A'}</div>
            <div className="m-bubble">
              <div className="m-meta">{m.role === 'user' ? ((session.name || 'Customer').split(' ')[0]) : window.__t('chat.assistant')} · {m.time}</div>
              {m.kind === 'voice' ? (
                <VoiceBubble
                  audioUrl={m.audioUrl}
                  audioPath={m.audioPath}
                  transcription={m.transcription}
                  pending={m.pending}
                  error={m.error}
                />
              ) : (
                <MessageText text={m.text} />
              )}
            </div>
          </div>
        ))}
        {isTyping && (
          <div className="msg ai">
            <div className="m-avatar">A</div>
            <div className="m-bubble">
              <div className="m-meta">Assistant · thinking</div>
              <div className="m-text"><span className="typing"><span /><span /><span /></span></div>
            </div>
          </div>
        )}
        <div ref={bottomRef} style={{ height: 1 }} />
      </div>

      <div className="chat-input">
        <div className={`wrap ${recordState === 'recording' ? 'is-recording' : ''}`}>
          {recordState === 'recording' ? (
            <>
              <button
                className="tool rec-cancel"
                title="Cancel recording"
                onClick={cancelRecording}
              >
                <Icon name="xCircle" />
              </button>
              <div className="rec-meter">
                <span className="rec-dot" />
                <span className="rec-time mono">{fmtRecordTime(recordSeconds)}</span>
                <span className="rec-hint">Recording… click send to finish</span>
              </div>
              <button
                className="send"
                title="Stop & send"
                onClick={stopRecording}
              >
                <Icon name="send" />
              </button>
            </>
          ) : (
            <>
              <div className="tools">
                <button
                  className="tool"
                  title="Voice message"
                  onClick={startRecording}
                  disabled={isTyping || isMock || recordState !== 'idle'}
                >
                  <Icon name="mic" />
                </button>
              </div>
              <textarea
                value={input}
                onChange={e => setInput(e.target.value)}
                onKeyDown={onKey}
                placeholder={isMock ? window.__t('chat.demoReadonly') : (isTyping ? window.__t('chat.waitingAssistant') : window.__t('chat.typePlaceholder'))}
                rows={1}
                disabled={isTyping || isMock}
              />
              <button className="send" disabled={!input.trim() || isTyping || isMock} onClick={() => send(input)}>
                <Icon name="send" />
              </button>
            </>
          )}
        </div>
        <div className="mono" style={{ fontSize: 10, color: 'var(--fg-4)', textTransform: 'uppercase', letterSpacing: '0.12em', marginTop: 10, display: 'flex', justifyContent: 'space-between' }}>
          <span>{isMock ? window.__t('chat.demoReadonlyFoot') : window.__t('chat.enterToSend')}</span>
          <span>{isMock ? messages.length + ' ' + (window.__lang === 'ar' ? 'رسالة' : 'messages') : 'Session ' + session.phone}</span>
        </div>
      </div>
    </div>
  );
};

// ---- Main Chat ----
const Chat = () => {
  const [userId, setUserId] = useStateC(null);
  const [sessions, setSessions] = useStateC([]);
  const [activeId, setActiveId] = useStateC(null);
  const mockChats = (window.__mockChatsFor ? window.__mockChatsFor(window.__lang) : window.__mockChats) || [];

  useEffectC(() => {
    let cancelled = false;
    window.__api.getUser().then(u => {
      if (cancelled || !u) return;
      setUserId(u.id);
      const s = loadSessions(u.id);
      setSessions(s);
      setActiveId(s.length > 0 ? s[0].phone : (mockChats[0]?.id || null));
    });
    return () => { cancelled = true; };
  }, []);

  useEffectC(() => { if (userId) saveSessions(userId, sessions); }, [sessions, userId]);

  const handleNewConversation = async () => {
    if (!userId) return;
    const sessionPhone = newSessionId();
    const newSession = {
      phone: sessionPhone,
      name: '',
      language: 'en',
      lastActivity: new Date().toISOString(),
      lastMessagePreview: ''
      // No hardcoded greeting — the AI greets in its first reply
    };
    // Create the lead record (name/phone will be updated by the AI)
    try {
      await window.__api.sbInsert('re_leads', {
        phone: sessionPhone,
        name: 'New Lead',
        language: 'en',
        status: 'new',
        owner_id: userId
      });
    } catch (e) { console.warn('lead insert failed:', e); }
    setSessions(prev => [newSession, ...prev]);
    setActiveId(sessionPhone);
  };

  const bumpActivity = (phone, preview) => {
    setSessions(prev => prev.map(s => s.phone === phone
      ? { ...s, lastActivity: new Date().toISOString(), lastMessagePreview: (preview || '').slice(0, 80) }
      : s));
  };

  // Called by ChatPanel when it finds a real name/language in re_leads
  const syncSessionFromDb = (phone, updates) => {
    setSessions(prev => prev.map(s => s.phone === phone ? { ...s, ...updates } : s));
  };

  // Resolve active session — can be a real or mock one
  const activeSession = useMemoC(() => {
    if (!activeId) return null;
    const real = sessions.find(s => s.phone === activeId);
    if (real) return {
      ...real,
      // give the panel a way to bubble DB-synced fields (name/language) back up
      _onSync: (updates) => syncSessionFromDb(real.phone, updates)
    };
    const mock = mockChats.find(m => m.id === activeId);
    if (mock) return {
      phone: mock.id,
      name: mock.name,
      language: mock.language,
      isMock: true,
      scenario: mock.scenario,
      _preloadedMessages: mock.messages
    };
    return null;
  }, [activeId, sessions, mockChats]);

  const activeAnalysis = useMemoC(() => {
    if (!activeId) return null;
    const mock = mockChats.find(m => m.id === activeId);
    return mock?.analysis || null;
  }, [activeId, mockChats]);

  return (
    <div className="chat-shell view" style={{ minHeight: 0 }}>
      <SessionList
        sessions={sessions}
        mockChats={mockChats}
        activeId={activeId}
        onPick={setActiveId}
        onNew={handleNewConversation}
      />
      {activeSession ? (
        <ChatPanel key={activeSession.phone} session={activeSession} onBumpActivity={bumpActivity} />
      ) : (
        <div className="chat-main" style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ textAlign: 'center', color: 'var(--fg-3)' }}>
            <div className="mono" style={{ fontSize: 10, textTransform: 'uppercase', letterSpacing: '0.14em', marginBottom: 10 }}>{window.__t('chat.empty.crumb')}</div>
            <h2 style={{ margin: '0 0 12px', fontFamily: 'var(--serif, var(--sans))', fontWeight: 500 }}>{window.__t('chat.empty.title')}</h2>
            <p style={{ color: 'var(--fg-4)', marginBottom: 22 }}>{window.__t('chat.empty.body')}</p>
            <button data-onboarding-id="chat-new-btn" className="btn primary" onClick={handleNewConversation}><Icon name="plus" size={13} /> {window.__t('chat.newConversation')}</button>
          </div>
        </div>
      )}
      <AnalysisPanel analysis={activeAnalysis} session={activeSession} isMock={activeSession?.isMock} />
    </div>
  );
};

window.Chat = Chat;
