/* global React, Icon, useSelectedWorkspaceId */
const {
  useEffect: useEffectConversations,
  useMemo: useMemoConversations,
  useRef: useRefConversations,
  useState: useStateConversations,
} = React;

const EMPTY_COUNTS = { total: 0, unread: 0, needsHuman: 0, assigned: 0, closed: 0 };

// Lightweight custom dropdown so we fully control the option colors (calm gray
// text, red for the selected row) instead of the browser's native popup, whose
// dark-gray highlight band on the selected option could not be restyled.
function InboxSelect({ value, onChange, options, ariaLabel }) {
  const [open, setOpen] = useStateConversations(false);
  const ref = useRefConversations(null);
  const selected = options.find(option => option.value === value) || options[0];

  useEffectConversations(() => {
    if (!open) return undefined;
    const handleOutside = event => {
      if (ref.current && !ref.current.contains(event.target)) setOpen(false);
    };
    const handleKey = event => { if (event.key === 'Escape') setOpen(false); };
    document.addEventListener('mousedown', handleOutside);
    document.addEventListener('keydown', handleKey);
    return () => {
      document.removeEventListener('mousedown', handleOutside);
      document.removeEventListener('keydown', handleKey);
    };
  }, [open]);

  return (
    <div className="inbox-select" ref={ref}>
      <button
        type="button"
        className={'inbox-select-trigger' + (open ? ' is-open' : '')}
        aria-haspopup="listbox"
        aria-expanded={open}
        aria-label={ariaLabel}
        onClick={() => setOpen(current => !current)}
      >
        <span>{selected ? selected.label : ''}</span>
        <svg className="inbox-select-caret" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>
      </button>
      {open && (
        <div className="inbox-select-menu" role="listbox" aria-label={ariaLabel}>
          {options.map(option => (
            <button
              key={option.value}
              type="button"
              role="option"
              aria-selected={option.value === value}
              className={'inbox-select-option' + (option.value === value ? ' is-selected' : '')}
              onClick={() => { onChange(option.value); setOpen(false); }}
            >
              {option.label}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function ConversationsPage() {
  const workspaceId = useSelectedWorkspaceId();
  const currentUser = window.PluginChatBotCurrentUser || {};
  const canDelete = currentUser.role === 'owner' || currentUser.role === 'admin';
  const [items, setItems] = useStateConversations([]);
  const [agents, setAgents] = useStateConversations([]);
  const [counts, setCounts] = useStateConversations(EMPTY_COUNTS);
  const [pagination, setPagination] = useStateConversations({ page: 1, hasMore: false, total: 0 });
  const [selectedId, setSelectedId] = useStateConversations('');
  // Mobile-only pane toggle. Kept separate from selectedId so the desktop
  // auto-select of the first conversation doesn't force the thread open (which
  // would immediately re-open it after tapping the back button).
  const [mobileThreadOpen, setMobileThreadOpen] = useStateConversations(false);
  // Mobile kebab menu for the thread header actions (Return to AI / Close / Delete).
  const [actionsOpen, setActionsOpen] = useStateConversations(false);
  const [detail, setDetail] = useStateConversations(null);
  const [search, setSearch] = useStateConversations('');
  const [status, setStatus] = useStateConversations('active');
  const [agentId, setAgentId] = useStateConversations('all');
  const [channelType, setChannelType] = useStateConversations('all');
  // 'all' = every conversation (AI + human); 'human' = human-support only.
  const [inboxMode, setInboxMode] = useStateConversations('all');
  const [reply, setReply] = useStateConversations('');
  const [whatsappTemplateKey, setWhatsappTemplateKey] = useStateConversations('');
  const [whatsappTemplateValues, setWhatsappTemplateValues] = useStateConversations('');
  const [loading, setLoading] = useStateConversations(true);
  const [loadingMore, setLoadingMore] = useStateConversations(false);
  const [busy, setBusy] = useStateConversations(false);
  const [error, setError] = useStateConversations('');
  const [visitorTyping, setVisitorTyping] = useStateConversations(false);
  const selectedRef = useRefConversations('');
  const messagePaneRef = useRefConversations(null);
  const previousMessageCountRef = useRefConversations(0);
  const typingStopTimerRef = useRefConversations(null);
  const lastTypingSentRef = useRefConversations(0);

  selectedRef.current = selectedId;

  const visibleItems = useMemoConversations(() => items, [items]);
  const conversation = detail?.conversation || null;
  const messages = Array.isArray(detail?.messages) ? detail.messages : [];
  const whatsappTemplates = Array.isArray(detail?.whatsappTemplates)
    ? detail.whatsappTemplates.filter(item => item.status === 'approved')
    : [];
  const whatsappWindowOpen = conversation?.channelType !== 'whatsapp'
    || isServiceWindowOpen(conversation?.customerServiceWindowExpiresAt);
  const metaWindowOpen = !['messenger', 'instagram'].includes(conversation?.channelType)
    || isServiceWindowOpen(conversation?.customerServiceWindowExpiresAt);
  const selectedWhatsappTemplate = whatsappTemplates.find(
    item => getWhatsappTemplateKey(item) === whatsappTemplateKey
  ) || null;

  const loadList = async (silent = false, page = 1, append = false) => {
    if (!workspaceId) return;
    if (!silent) setLoading(true);
    if (append) setLoadingMore(true);

    try {
      const params = new URLSearchParams({
        workspaceId,
        limit: '40',
        page: String(page),
      });
      if (inboxMode === 'all') {
        params.set('scope', 'all');
      } else {
        params.set('status', status);
        if (agentId !== 'all') params.set('assignedAgentId', agentId);
        if (channelType !== 'all') params.set('channelType', channelType);
      }
      if (search.trim()) params.set('search', search.trim());

      const data = await fetch(`/api/conversations?${params.toString()}`, {
        credentials: 'include',
        cache: 'no-store',
      }).then(readConversationResponse);

      const nextItems = Array.isArray(data.conversations) ? data.conversations : [];
      setItems(current => append
        ? mergeConversationItems(current, nextItems)
        : nextItems
      );
      setAgents(Array.isArray(data.agents) ? data.agents : []);
      setCounts(data.counts || EMPTY_COUNTS);
      setPagination(data.pagination || { page, hasMore: false, total: nextItems.length });
      setError(data.migrationRequired
        ? 'Run the live chat database migrations before using the inbox.'
        : ''
      );

      if (!append) {
        const query = new URLSearchParams(window.location.search);
        const requestedConversationId = query.get('conversation')
          || window.sessionStorage.getItem('pluginchatbot_open_conversation')
          || '';

        if (requestedConversationId) {
          window.sessionStorage.removeItem('pluginchatbot_open_conversation');
          query.delete('conversation');
          const nextSearch = query.toString();
          window.history.replaceState({}, '', `${window.location.pathname}${nextSearch ? `?${nextSearch}` : ''}`);
          setSelectedId(requestedConversationId);
        } else if (!selectedRef.current && nextItems[0]) {
          setSelectedId(nextItems[0].conversationId);
        } else if (selectedRef.current && !nextItems.some(item => item.conversationId === selectedRef.current)) {
          setSelectedId(nextItems[0]?.conversationId || '');
        }
      }
    } catch (loadError) {
      setError(loadError.message || 'Unable to load conversations.');
    } finally {
      if (!silent) setLoading(false);
      if (append) setLoadingMore(false);
    }
  };

  const loadDetail = async (conversationId, silent = false) => {
    if (!workspaceId || !conversationId) {
      setDetail(null);
      return;
    }

    try {
      const data = await fetch(
        `/api/conversations/${encodeURIComponent(conversationId)}?workspaceId=${encodeURIComponent(workspaceId)}`,
        { credentials: 'include', cache: 'no-store' }
      ).then(readConversationResponse);

      if (selectedRef.current === conversationId) {
        setDetail(data);
        setVisitorTyping(Boolean(data.typing?.visitorTyping));
        setError('');
      }
    } catch (loadError) {
      if (!silent) setError(loadError.message || 'Unable to load this conversation.');
    }
  };

  const loadTyping = async conversationId => {
    if (!workspaceId || !conversationId) {
      setVisitorTyping(false);
      return;
    }

    try {
      const data = await fetch(
        `/api/conversations/${encodeURIComponent(conversationId)}/typing?workspaceId=${encodeURIComponent(workspaceId)}`,
        { credentials: 'include', cache: 'no-store' }
      ).then(readConversationResponse);

      if (selectedRef.current === conversationId) {
        setVisitorTyping(Boolean(data.typing?.visitorTyping));
      }
    } catch (typingError) {
      setVisitorTyping(false);
    }
  };

  const sendAgentTyping = (isTyping, conversationId = selectedRef.current) => {
    if (!workspaceId || !conversationId || conversation?.channelType !== 'website') return;

    fetch(`/api/conversations/${encodeURIComponent(conversationId)}/typing`, {
      method: 'POST',
      credentials: 'include',
      headers: { 'content-type': 'application/json' },
      body: JSON.stringify({ workspaceId, isTyping: Boolean(isTyping) }),
    }).catch(() => {});
  };

  useEffectConversations(() => {
    setSelectedId('');
    setDetail(null);
    setPagination({ page: 1, hasMore: false, total: 0 });
    loadList(false, 1, false);
  }, [workspaceId, status, agentId, channelType, inboxMode]);

  useEffectConversations(() => {
    const timer = window.setTimeout(() => {
      setSelectedId('');
      setDetail(null);
      loadList(false, 1, false);
    }, 300);
    return () => window.clearTimeout(timer);
  }, [search]);

  useEffectConversations(() => {
    if (!selectedId) {
      setDetail(null);
      setVisitorTyping(false);
      previousMessageCountRef.current = 0;
      return undefined;
    }

    setVisitorTyping(false);
    setReply('');
    setWhatsappTemplateKey('');
    setWhatsappTemplateValues('');
    loadDetail(selectedId);
    loadTyping(selectedId);
    return undefined;
  }, [selectedId, workspaceId]);

  useEffectConversations(() => {
    if (!workspaceId) return undefined;

    const timer = window.setInterval(() => {
      loadList(true, 1, false);
      if (selectedRef.current) loadDetail(selectedRef.current, true);
    }, 4000);

    return () => window.clearInterval(timer);
  }, [workspaceId, status, agentId, channelType, search, inboxMode]);

  useEffectConversations(() => {
    if (!workspaceId || !selectedId) return undefined;

    const timer = window.setInterval(() => {
      loadTyping(selectedId);
    }, 1000);

    return () => window.clearInterval(timer);
  }, [workspaceId, selectedId]);

  useEffectConversations(() => {
    const typingConversationId = selectedId;

    return () => {
      if (typingStopTimerRef.current) {
        window.clearTimeout(typingStopTimerRef.current);
      }
      if (typingConversationId) {
        sendAgentTyping(false, typingConversationId);
      }
    };
  }, [workspaceId, selectedId]);

  useEffectConversations(() => {
    if (!messagePaneRef.current) return;
    if (messages.length !== previousMessageCountRef.current || visitorTyping) {
      messagePaneRef.current.scrollTop = messagePaneRef.current.scrollHeight;
      previousMessageCountRef.current = messages.length;
    }
  }, [messages.length, selectedId, visitorTyping]);

  const runAction = async (action, payload = {}) => {
    if (!selectedId || busy) return;
    setBusy(true);
    setError('');

    try {
      const data = await fetch(`/api/conversations/${encodeURIComponent(selectedId)}/${action}`, {
        method: 'POST',
        credentials: 'include',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ workspaceId, ...payload }),
      }).then(readConversationResponse);

      if (data.summary !== undefined) {
        setDetail(current => current ? {
          ...current,
          conversation: { ...current.conversation, summary: data.summary },
        } : current);
      }

      await Promise.all([
        loadList(true, 1, false),
        loadDetail(selectedId, true),
      ]);
    } catch (actionError) {
      setError(actionError.message || 'Unable to update this conversation.');
    } finally {
      setBusy(false);
    }
  };

  const deleteConversation = async () => {
    if (!selectedId || !canDelete || busy) return;
    const name = getConversationName(conversation);
    const confirmed = window.confirm(
      `Delete the conversation with ${name}? The lead will remain in Leads, but this conversation will be removed from the inbox.`
    );
    if (!confirmed) return;

    setBusy(true);
    setError('');

    try {
      await fetch(`/api/conversations/${encodeURIComponent(selectedId)}`, {
        method: 'DELETE',
        credentials: 'include',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ workspaceId }),
      }).then(readConversationResponse);

      setSelectedId('');
      setDetail(null);
      await loadList(true, 1, false);
    } catch (deleteError) {
      setError(deleteError.message || 'Unable to delete this conversation.');
    } finally {
      setBusy(false);
    }
  };

  const handleReplyChange = event => {
    const value = event.target.value;
    setReply(value);

    const canSignalTyping = conversation
      && conversation.channelType === 'website'
      && conversation.status !== 'closed'
      && !conversation.aiEnabled;

    if (!canSignalTyping) return;

    if (typingStopTimerRef.current) {
      window.clearTimeout(typingStopTimerRef.current);
    }

    if (!value.trim()) {
      sendAgentTyping(false);
      lastTypingSentRef.current = 0;
      return;
    }

    const now = Date.now();
    if (now - lastTypingSentRef.current > 1800) {
      sendAgentTyping(true);
      lastTypingSentRef.current = now;
    }

    typingStopTimerRef.current = window.setTimeout(() => {
      sendAgentTyping(false);
      lastTypingSentRef.current = 0;
    }, 1400);
  };

  const sendReply = async event => {
    event.preventDefault();

    if (conversation?.channelType === 'whatsapp' && !whatsappWindowOpen) {
      if (!selectedWhatsappTemplate) {
        setError('Select an approved WhatsApp template first.');
        return;
      }

      const templateVariables = parseTemplateValues(whatsappTemplateValues);
      const requiredVariables = Array.isArray(selectedWhatsappTemplate.variables)
        ? selectedWhatsappTemplate.variables.length
        : 0;
      if (templateVariables.length < requiredVariables) {
        setError(`This template needs ${requiredVariables} value${requiredVariables === 1 ? '' : 's'}. Enter one value per line.`);
        return;
      }

      setWhatsappTemplateValues('');
      await runAction('reply', {
        templateName: selectedWhatsappTemplate.name,
        templateLanguage: selectedWhatsappTemplate.language,
        templateVariables: templateVariables.slice(0, requiredVariables),
      });
      return;
    }

    const message = reply.trim();
    if (!message) return;

    if (typingStopTimerRef.current) {
      window.clearTimeout(typingStopTimerRef.current);
    }
    sendAgentTyping(false);
    lastTypingSentRef.current = 0;
    setReply('');
    await runAction('reply', { message });
  };

  return (
    <div className={`live-inbox-shell ${mobileThreadOpen ? 'is-thread-open' : ''}`}>
      <section className="live-inbox-list">
        <div className="live-inbox-title">
          <div>
            <h2>{inboxMode === 'all' ? 'Conversations' : 'Human Support Inbox'}</h2>
            <p>{inboxMode === 'all' ? 'All chatbot conversations, including AI-only chats' : 'Only visitor-requested live chats appear here'}</p>
          </div>
          <button type="button" className="icon-button" onClick={() => loadList(false, 1, false)} aria-label="Refresh inbox">↻</button>
        </div>

        <div className="live-inbox-modes">
          <button type="button" className={inboxMode === 'all' ? 'is-active' : ''} onClick={() => setInboxMode('all')}>All chats</button>
          <button type="button" className={inboxMode === 'human' ? 'is-active' : ''} onClick={() => setInboxMode('human')}>Human support</button>
        </div>

        {inboxMode === 'human' && (
          <div className="live-inbox-counts">
            <button className={status === 'active' ? 'is-active' : ''} onClick={() => setStatus('active')}>Active · {counts.total}</button>
            <button className={status === 'needs_human' ? 'is-active' : ''} onClick={() => setStatus('needs_human')}>Waiting · {counts.needsHuman}</button>
            <button className={status === 'assigned' ? 'is-active' : ''} onClick={() => setStatus('assigned')}>Assigned · {counts.assigned}</button>
            <button className={status === 'closed' ? 'is-active' : ''} onClick={() => setStatus('closed')}>Closed · {counts.closed}</button>
          </div>
        )}

        <div className="live-inbox-filters">
          <input value={search} onChange={event => setSearch(event.target.value)} placeholder="Search name, email, phone, message" />
          {inboxMode === 'human' && (
            <React.Fragment>
              <InboxSelect
                ariaLabel="Filter by agent"
                value={agentId}
                onChange={setAgentId}
                options={[{ value: 'all', label: 'All agents' }, ...agents.map(agent => ({ value: agent.userId, label: agent.fullName }))]}
              />
              <InboxSelect
                ariaLabel="Filter by channel"
                value={channelType}
                onChange={setChannelType}
                options={[
                  { value: 'all', label: 'All channels' },
                  { value: 'website', label: 'Website chat' },
                  { value: 'whatsapp', label: 'WhatsApp' },
                  { value: 'messenger', label: 'Messenger' },
                  { value: 'instagram', label: 'Instagram' },
                ]}
              />
            </React.Fragment>
          )}
        </div>

        {error && <div className="live-inbox-error">{error}</div>}

        <div className="live-inbox-items">
          {loading && <div className="live-inbox-empty">Loading conversations…</div>}
          {!loading && visibleItems.length === 0 && <div className="live-inbox-empty">{inboxMode === 'all' ? 'No conversations yet.' : 'No human support conversations found.'}</div>}
          {visibleItems.map(item => (
            <button
              key={item.conversationId}
              type="button"
              className={`live-inbox-item ${selectedId === item.conversationId ? 'is-active' : ''}`}
              onClick={() => { setSelectedId(item.conversationId); setMobileThreadOpen(true); }}
            >
              <span className="live-inbox-avatar">{getConversationInitial(item)}</span>
              <span className="live-inbox-item-copy">
                <strong>
                  {getConversationName(item)}
                  {item.isAi && <span className="live-inbox-ai-badge">AI</span>}
                </strong>
                <small>{item.lastMessage || (item.isAi ? 'AI conversation' : 'Human support requested')}</small>
                <em>{item.isAi ? 'AI chat' : `${formatConversationStatus(item.status)} · ${formatChannelLabel(item.channelType)}`}</em>
              </span>
              <span className="live-inbox-item-meta">
                <time>{formatRelativeTime(item.lastMessageAt)}</time>
                {item.unreadCount > 0 && <b>{item.unreadCount}</b>}
              </span>
            </button>
          ))}
          {pagination.hasMore && (
            <button
              type="button"
              className="live-inbox-load-more"
              onClick={() => loadList(true, Number(pagination.page || 1) + 1, true)}
              disabled={loadingMore}
            >
              {loadingMore ? 'Loading…' : 'Load more conversations'}
            </button>
          )}
        </div>
      </section>

      <section className="live-inbox-thread">
        {!conversation && <div className="live-inbox-placeholder">Select a human support conversation.</div>}

        {conversation && (
          <>
            <header className="live-thread-header">
              <button type="button" className="live-thread-back" onClick={() => setMobileThreadOpen(false)} aria-label="Back to inbox">←</button>
              <div>
                <strong>{getConversationName(conversation)}</strong>
                <span>via {formatChannelLabel(conversation.channelType)} · {conversation.templateName}</span>
              </div>
              {!conversation.isAi && (
                <div className="live-thread-actions-wrap">
                  <button
                    type="button"
                    className="live-thread-menu-btn"
                    onClick={() => setActionsOpen(open => !open)}
                    aria-label="Conversation actions"
                    aria-expanded={actionsOpen}
                  >⋮</button>
                  <div
                    className={`live-thread-actions ${actionsOpen ? 'is-open' : ''}`}
                    onClick={() => setActionsOpen(false)}
                  >
                    {conversation.status !== 'closed' && !conversation.aiEnabled && (
                      <button type="button" onClick={() => runAction('return-to-ai')} disabled={busy}>Return to AI</button>
                    )}
                    {conversation.status === 'closed'
                      ? <button type="button" onClick={() => runAction('reopen')} disabled={busy}>Reopen</button>
                      : <button type="button" onClick={() => runAction('close')} disabled={busy}>Close</button>}
                    {canDelete && (
                      <button type="button" className="is-danger" onClick={deleteConversation} disabled={busy}>Delete</button>
                    )}
                  </div>
                </div>
              )}
            </header>

            <div className="live-thread-messages" ref={messagePaneRef}>
              {messages.map(message => (
                <div
                  key={message.messageId || message.id}
                  className={`live-thread-message is-${message.senderType}`}
                  style={{
                    display: 'flex',
                    width: '100%',
                    justifyContent: message.senderType === 'visitor' ? 'flex-start' : 'flex-end',
                    background: 'transparent',
                  }}
                >
                  <div
                    style={{
                      display: 'inline-flex',
                      flexDirection: 'column',
                      alignItems: message.senderType === 'visitor' ? 'flex-start' : 'flex-end',
                      width: 'fit-content',
                      maxWidth: '72%',
                      borderRadius: 14,
                      padding: '10px 12px',
                      background: message.senderType === 'visitor'
                        ? '#111111'
                        : '#D92F24',
                      color: '#ffffff',
                    }}
                  >
                    <span>{message.body}</span>
                    <time>{formatMessageTime(message.createdAt)}{formatDeliveryStatus(message, conversation)}</time>
                  </div>
                </div>
              ))}
              {visitorTyping && conversation.channelType === 'website' && conversation.status !== 'closed' && !conversation.aiEnabled && (
                <div className="live-thread-typing" role="status" aria-live="polite">
                  <span className="live-thread-typing-dots" aria-hidden="true">
                    <i></i><i></i><i></i>
                  </span>
                  <span>{getConversationName(conversation)} is typing…</span>
                </div>
              )}
            </div>

            {conversation.isAi ? (
              <div className="live-thread-reply" style={{display:'block', fontSize:12, lineHeight:1.6, color:'var(--pc-text-muted)'}}>
                This is an AI conversation (read-only). To reply, the visitor must request human support from the chat widget.
              </div>
            ) : conversation.channelType === 'whatsapp' && !whatsappWindowOpen ? (
              <form className="live-thread-reply" onSubmit={sendReply} style={{alignItems:'stretch', flexDirection:'column', gap:8}}>
                <div style={{fontSize:12, lineHeight:1.5, color:'var(--pc-text-muted)'}}>
                  The 24-hour reply window is closed. Select an approved template to contact this customer.
                </div>
                <select
                  value={whatsappTemplateKey}
                  onChange={event => {
                    setWhatsappTemplateKey(event.target.value);
                    setWhatsappTemplateValues('');
                    setError('');
                  }}
                  disabled={busy}
                >
                  <option value="">Select an approved template</option>
                  {whatsappTemplates.map(template => (
                    <option key={getWhatsappTemplateKey(template)} value={getWhatsappTemplateKey(template)}>
                      {template.name} · {template.language}
                    </option>
                  ))}
                </select>
                {selectedWhatsappTemplate && selectedWhatsappTemplate.variables?.length > 0 && (
                  <textarea
                    value={whatsappTemplateValues}
                    onChange={event => setWhatsappTemplateValues(event.target.value)}
                    placeholder={`Enter ${selectedWhatsappTemplate.variables.length} template value${selectedWhatsappTemplate.variables.length === 1 ? '' : 's'}, one per line`}
                    rows={Math.min(5, Math.max(2, selectedWhatsappTemplate.variables.length))}
                    maxLength={4000}
                    disabled={busy}
                  />
                )}
                {selectedWhatsappTemplate?.body && (
                  <div style={{fontSize:12, lineHeight:1.5, color:'var(--pc-text-muted)'}}>
                    Preview: {selectedWhatsappTemplate.body}
                  </div>
                )}
                <button type="submit" disabled={busy || !selectedWhatsappTemplate}>
                  <Icon name="send" size={15}/>Send Template
                </button>
              </form>
            ) : (['messenger', 'instagram'].includes(conversation.channelType) && !metaWindowOpen) ? (
              <div className="live-thread-reply" style={{display:'block', fontSize:12, lineHeight:1.6, color:'var(--pc-text-muted)'}}>
                The reply window is closed. The customer must message the business again before an agent can reply.
              </div>
            ) : (
              <form className="live-thread-reply" onSubmit={sendReply}>
                <input
                  value={reply}
                  onChange={handleReplyChange}
                  placeholder={conversation.status === 'closed' ? 'Reply to reopen this conversation…' : 'Reply as a human…'}
                  maxLength={4000}
                />
                <button type="submit" disabled={busy || !reply.trim()}><Icon name="send" size={15}/>Send</button>
              </form>
            )}
          </>
        )}
      </section>

      <aside className="live-inbox-details">
        {!conversation && <div className="live-inbox-placeholder">Visitor details will appear here.</div>}
        {conversation && (
          <>
            <div className="live-detail-section">
              <span className="live-detail-label">Visitor</span>
              <h3>{getConversationName(conversation)}{conversation.isAi && <span className="live-inbox-ai-badge">AI</span>}</h3>
              <p>{conversation.contact?.phone || 'No phone number'}</p>
              <p>{conversation.contact?.email || 'No email address'}</p>
            </div>

            {detail?.visitorStats && (
              <div className="live-detail-section">
                <span className="live-detail-label">History</span>
                <div className="live-detail-stats">
                  <div><b>{detail.visitorStats.chats || 0}</b><span>Chats</span></div>
                  <div><b>{detail.visitorStats.firstSeen ? formatRelativeTime(detail.visitorStats.firstSeen) : '—'}</b><span>First seen</span></div>
                  <div><b>{detail.visitorStats.lastSeen ? formatRelativeTime(detail.visitorStats.lastSeen) : '—'}</b><span>Last seen</span></div>
                </div>
              </div>
            )}

            <div className="live-detail-section">
              <span className="live-detail-label">Status</span>
              <span className={`live-status is-${conversation.status}`}>{conversation.isAi ? 'AI chat' : formatConversationStatus(conversation.status)}</span>
            </div>

            <div className="live-detail-section">
              <span className="live-detail-label">Channel</span>
              <p>{formatChannelLabel(conversation.channelType)}</p>
              {conversation.channelType === 'whatsapp' && (
                <p>{whatsappWindowOpen ? 'Free-form replies are currently allowed.' : 'Approved template required for the next reply.'}</p>
              )}
              {['messenger', 'instagram'].includes(conversation.channelType) && (
                <p>{metaWindowOpen ? 'Agent replies are currently allowed.' : 'The customer must message again before an agent can reply.'}</p>
              )}
            </div>

            {!conversation.isAi && (
              <div className="live-detail-section">
                <label className="live-detail-label" htmlFor="conversation-agent">Assigned agent</label>
                <select
                  id="conversation-agent"
                  value={conversation.assignedAgentId || ''}
                  onChange={event => runAction('assign', { agentId: event.target.value })}
                  disabled={busy || conversation.status === 'closed'}
                >
                  <option value="">Unassigned</option>
                  {agents.map(agent => (
                    <option key={agent.userId} value={agent.userId}>
                      {agent.fullName}{agent.department ? ` · ${agent.department}` : ''}
                    </option>
                  ))}
                </select>
              </div>
            )}

            <div className="live-detail-section">
              <span className="live-detail-label">Source page</span>
              <p className="live-detail-break">{conversation.sourceUrl || 'Not recorded'}</p>
            </div>

            {!conversation.isAi && (
              <div className="live-detail-section">
                <div className="live-detail-heading">
                  <span className="live-detail-label">AI summary</span>
                  <button type="button" onClick={() => runAction('summary')} disabled={busy}>Regenerate</button>
                </div>
                <div className="live-summary">{conversation.summary || 'Summary will be generated when the visitor requests human support.'}</div>
              </div>
            )}

            <div className="live-detail-section">
              <span className="live-detail-label">Consent</span>
              <p>{conversation.contact?.consentStatus === 'opted_in' ? 'Contact consent provided' : 'No explicit contact consent recorded'}</p>
            </div>
          </>
        )}
      </aside>
    </div>
  );
}

async function readConversationResponse(response) {
  const data = await response.json().catch(() => ({}));
  const authenticationRequired = response.status === 401
    && String(data.error || '').trim().toLowerCase() === 'authentication required.';
  if (authenticationRequired) {
    const isLocalhost = ['localhost', '127.0.0.1'].includes(window.location.hostname);
    if (!isLocalhost) {
      window.location.href = (['localhost','127.0.0.1'].includes(location.hostname)?'http://localhost:8788':'https://pluginchatbot.com')+'/login.html?redirect=' + encodeURIComponent(window.location.href);
    }
    throw new Error(isLocalhost ? 'Your local dashboard session has expired.' : 'Authentication required.');
  }
  if (!response.ok || !data.ok) {
    const error = new Error(data.error || 'Conversation request failed.');
    error.code = data.code || '';
    error.templates = Array.isArray(data.templates) ? data.templates : [];
    throw error;
  }
  return data;
}

function mergeConversationItems(current, incoming) {
  const items = new Map();
  [...current, ...incoming].forEach(item => items.set(item.conversationId, item));
  return Array.from(items.values());
}

function getConversationName(conversation) {
  return conversation?.contact?.name
    || conversation?.contact?.email
    || conversation?.contact?.phone
    || 'Website visitor';
}

function getConversationInitial(conversation) {
  return getConversationName(conversation).slice(0, 1).toUpperCase();
}

function formatConversationStatus(value) {
  const labels = {
    pending: 'Pending',
    needs_human: 'Waiting for human',
    assigned: 'Assigned',
    closed: 'Closed',
  };
  return labels[value] || 'Human support';
}

function formatChannelLabel(value) {
  const labels = {
    website: 'Website chat',
    whatsapp: 'WhatsApp',
    messenger: 'Messenger',
    instagram: 'Instagram',
  };
  return labels[value] || 'Messaging channel';
}

function isServiceWindowOpen(value) {
  const expiry = parseServerDate(value).getTime();
  return Number.isFinite(expiry) && expiry > Date.now();
}

function getWhatsappTemplateKey(template) {
  return `${template?.name || ''}::${template?.language || 'en_US'}`;
}

function parseTemplateValues(value) {
  return String(value || '')
    .split(/\r?\n/)
    .map(item => item.trim())
    .filter(Boolean)
    .slice(0, 20);
}

function formatDeliveryStatus(message, conversation) {
  if (conversation?.channelType === 'website' || message?.senderType !== 'agent') return '';
  const status = String(message?.deliveryStatus || '').trim();
  return status ? ` · ${status}` : '';
}

// Server timestamps come back as UTC without a zone suffix (e.g.
// "2026-07-28 08:44:58"). Browsers parse that space-separated form as LOCAL
// time, which skews every relative/display time by the viewer's UTC offset.
// Normalize to an explicit UTC instant before constructing the Date.
function parseServerDate(value) {
  const raw = String(value || '').trim();
  if (!raw) return new Date(NaN);
  const normalized = /[zZ]|[+-]\d{2}:?\d{2}$/.test(raw) ? raw : `${raw.replace(' ', 'T')}Z`;
  return new Date(normalized);
}

function formatRelativeTime(value) {
  const time = parseServerDate(value).getTime();
  if (!Number.isFinite(time)) return '';
  const seconds = Math.max(0, Math.round((Date.now() - time) / 1000));
  if (seconds < 60) return 'now';
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h`;
  return `${Math.floor(seconds / 86400)}d`;
}

function formatMessageTime(value) {
  const date = parseServerDate(value);
  if (Number.isNaN(date.getTime())) return '';
  return date.toLocaleString([], {
    month: 'short',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
  });
}

window.ConversationsPage = ConversationsPage;
