import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Icon, IconButton, Icons, Text } from 'folds';
import { Room } from 'matrix-js-sdk';
import { PowerLevelsContextProvider, usePowerLevels } from '../../hooks/usePowerLevels';
import { useEditor } from '../../components/editor';
import { ThreadDrawer } from '../room/ThreadDrawer';
import { RoomInput } from '../room/RoomInput';
import { ChatComposer } from '../room/RoomView.css';
import { getBotPath, getBotThreadPath } from '../../pages/pathUtils';
import { today, timeHourMinute, timeDayMonYear } from '../../utils/time';
import { useBotConversations } from './useBotConversations';
import { AiChatHeader } from './AiChatHeader';
import type { BotPreset } from './catalog';
import * as css from './BotConversations.css';

// Compact last-activity stamp for a history row: time-of-day for today's chats, date otherwise.
// Reuses the shared time helpers so it follows the user's 12/24h + locale settings.
const formatConversationTime = (ts: number): string =>
  today(ts) ? timeHourMinute(ts) : timeDayMonYear(ts);

type BotConversationsProps = {
  preset: BotPreset;
  room: Room;
  // The open conversation's thread root, from /bots/:botId/thread/:rootId. Absent → the
  // new-chat composer (the default landing — every open of the bot starts a fresh chat).
  rootId?: string;
};

// New-chat composer — the default view when no conversation is open. It mounts the STANDARD
// RoomInput (markdown / mentions / uploads / commands), sending TOP-LEVEL (threadId undefined).
// The always-on backend roots a thread on that first message and answers inside, so we navigate
// into the freshly-rooted thread the moment the send resolves — the just-sent event id IS the
// new conversation's root, delivered via RoomInput's onSend callback.
function NewChatComposer({ preset, room }: { preset: BotPreset; room: Room }) {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const editor = useEditor();
  const fileDropRef = useRef<HTMLDivElement>(null);
  // Navigate exactly once: a fast double-send (Enter + click) must not double-navigate.
  const navigatedRef = useRef(false);
  const handleSend = useCallback(
    (eventId: string) => {
      if (navigatedRef.current) return;
      navigatedRef.current = true;
      navigate(getBotThreadPath(preset.id, eventId));
    },
    [navigate, preset.id]
  );

  return (
    <div className={css.NewChat}>
      <div className={css.NewChatHint}>
        <Text size="T300" priority="400" align="Center">
          {t('Bots.conversations.new_chat_hint')}
        </Text>
      </div>
      <div ref={fileDropRef} className={`${css.ComposerWrap} ${ChatComposer}`}>
        <RoomInput
          room={room}
          editor={editor}
          roomId={room.roomId}
          fileDropContainerRef={fileDropRef}
          onSend={handleSend}
          textOnly
          singleRow
        />
      </div>
    </div>
  );
}

// ChatGPT-style conversation surface for an assistant bot's control DM. The bot hero (avatar /
// name / handle / description + ⋮ menu, which carries the "show raw chat" escape hatch) tops the
// surface, with a leading back-chevron and trailing "New chat" + "History" controls embedded in
// it. Opening the bot lands on a fresh new-chat composer; sending roots a thread and opens it
// (the reused ThreadDrawer in `assistant` style — full-width bot text, user bubbles, no avatars).
// Past chats live in a half-window panel that slides in from the RIGHT. Reached only for
// `experience.type === 'ai-chat'` bots (BotExperienceRoute decides); bridge bots never get here.
export function BotConversations({ preset, room, rootId }: BotConversationsProps) {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const conversations = useBotConversations(room);
  // ThreadDrawer + the new-chat RoomInput both read usePowerLevelsContext; the bot surface has
  // no provider of its own (Room.tsx supplies one in channels), so wrap the whole surface here.
  // RoomProvider + IsOneOnOneProvider come from BotRoomProvider above.
  const powerLevels = usePowerLevels(room);
  const [historyOpen, setHistoryOpen] = useState(false);

  // Close the history panel whenever the route changes (a chat was picked, or "New chat" was
  // tapped), so the panel slides away and reveals the chosen surface.
  useEffect(() => {
    setHistoryOpen(false);
  }, [rootId]);

  // Keep the CLOSED history panel out of the tab order and the a11y tree. The <aside> stays mounted
  // so it can slide, but while closed it's only translated off-screen — without this its ✕ /
  // New-chat / conversation-row buttons stay keyboard-focusable behind the chat. Set via ref
  // because React 18.2's JSX typing doesn't expose `inert` (same technique as MobileTabsLayout's
  // BaseLayer); `inert` removes the subtree from focus, hit-testing and assistive tech at once.
  const historyPanelRef = useRef<HTMLElement>(null);
  useEffect(() => {
    if (historyPanelRef.current) historyPanelRef.current.inert = !historyOpen;
  }, [historyOpen]);

  const goNewChat = useCallback(() => {
    setHistoryOpen(false);
    navigate(getBotPath(preset.id));
  }, [navigate, preset.id]);

  const openConversation = useCallback(
    (rid: string) => {
      // Close the panel directly rather than leaning on the rootId effect: re-tapping the
      // already-open conversation doesn't change rootId, so the effect wouldn't fire and the
      // panel would stay open over the chat the user just asked to see.
      setHistoryOpen(false);
      navigate(getBotThreadPath(preset.id, rid));
    },
    [navigate, preset.id]
  );

  return (
    <PowerLevelsContextProvider value={powerLevels}>
      <div className={css.Surface}>
        <AiChatHeader
          preset={preset}
          room={room}
          onNewChat={goNewChat}
          onOpenHistory={() => setHistoryOpen(true)}
        />
        {/* Reset --vojo-safe-top for everything below the hero: css.Surface already paid the
          status-bar inset for the whole surface, and the reused ThreadDrawerMobile re-applies
          `padding-top: var(--vojo-safe-top)` (it was written for the /channels case where the
          drawer sits at the screen top with no header above it). Without this reset that inset
          double-counts on native and paints an empty band under the header. Mirrors Modal500 /
          MobileMediaViewerHorseshoe / ChannelsWorkspaceHorseshoe. */}
        <div className={css.Body} style={{ ['--vojo-safe-top' as string]: '0px' }}>
          {historyOpen && (
            <button
              type="button"
              className={css.Backdrop}
              aria-label={t('Bots.conversations.back')}
              onClick={() => setHistoryOpen(false)}
            />
          )}
          <aside ref={historyPanelRef} className={css.History} data-open={historyOpen || undefined}>
            <div className={css.HistoryHeader}>
              <Text className={css.HistoryTitle} size="H4" truncate>
                {t('Bots.conversations.title')}
              </Text>
              <IconButton
                size="300"
                variant="Background"
                radii="300"
                onClick={() => setHistoryOpen(false)}
                aria-label={t('Bots.conversations.back')}
              >
                <Icon src={Icons.Cross} />
              </IconButton>
            </div>
            <button type="button" className={css.NewChatButton} onClick={goNewChat}>
              <Icon size="100" src={Icons.Plus} filled />
              <span>{t('Bots.conversations.new_chat')}</span>
            </button>
            <div className={css.HistoryList}>
              {conversations.length === 0 ? (
                <div className={css.Empty}>
                  <Text size="T300" priority="400" align="Center">
                    {t('Bots.conversations.empty')}
                  </Text>
                </div>
              ) : (
                conversations.map((conversation) => (
                  <button
                    key={conversation.rootId}
                    type="button"
                    className={css.Row}
                    data-active={conversation.rootId === rootId || undefined}
                    onClick={() => openConversation(conversation.rootId)}
                  >
                    <Text className={css.RowTitle} as="span" size="T300">
                      {conversation.title || t('Bots.conversations.untitled')}
                    </Text>
                    {conversation.ts > 0 && (
                      <Text className={css.RowTime} as="span" size="T200">
                        {formatConversationTime(conversation.ts)}
                      </Text>
                    )}
                  </button>
                ))
              )}
            </div>
          </aside>

          <div className={css.Main} data-history-open={historyOpen || undefined}>
            {rootId ? (
              <ThreadDrawer
                key={`${room.roomId}/${rootId}`}
                room={room}
                rootId={rootId}
                parentRoomPath={getBotPath(preset.id)}
                variant="mobile"
                hideHeader
                messageStyle="assistant"
              />
            ) : (
              <NewChatComposer preset={preset} room={room} />
            )}
          </div>
        </div>
      </div>
    </PowerLevelsContextProvider>
  );
}
