import React, { useState, useCallback, useEffect, useMemo } from 'react';
import { Box, useApp, useInput } from 'ink';
import { TopBar } from './components/top-bar.js';
import { BottomBar } from './components/bottom-bar.js';
import { MessageList } from './components/message-list.js';
import { InputBar } from './components/input-bar.js';
import { Sidebar } from './components/sidebar.js';
import { SearchBar } from './components/search-bar.js';
import { useSocket } from './hooks/use-socket.js';
import { useGitInfo } from './hooks/use-git-info.js';
import { useViewport } from './hooks/use-viewport.js';
import { useAppMode } from './hooks/use-app-mode.js';
import { useConversations } from './hooks/use-conversations.js';
import { useSearch } from './hooks/use-search.js';

export interface TuiAppProps {
  gatewayUrl: string;
  conversationId?: string;
  sessionCookie?: string;
  initialModel?: string;
  initialProvider?: string;
  agentId?: string;
  agentName?: string;
  projectId?: string;
}

export function TuiApp({
  gatewayUrl,
  conversationId,
  sessionCookie,
  initialModel,
  initialProvider,
  agentId,
  agentName,
  projectId: _projectId,
}: TuiAppProps) {
  const { exit } = useApp();
  const gitInfo = useGitInfo();
  const appMode = useAppMode();

  const socket = useSocket({
    gatewayUrl,
    sessionCookie,
    initialConversationId: conversationId,
    initialModel,
    initialProvider,
    agentId,
  });

  const conversations = useConversations({ gatewayUrl, sessionCookie });

  const viewport = useViewport({ totalItems: socket.messages.length });

  const search = useSearch(socket.messages);

  // Scroll to current match when it changes
  const currentMatch = search.matches[search.currentMatchIndex];
  useEffect(() => {
    if (currentMatch && appMode.mode === 'search') {
      viewport.scrollTo(currentMatch.messageIndex);
    }
  }, [currentMatch, appMode.mode, viewport]);

  // Compute highlighted message indices for MessageList
  const highlightedMessageIndices = useMemo(() => {
    if (search.matches.length === 0) return undefined;
    return new Set(search.matches.map((m) => m.messageIndex));
  }, [search.matches]);

  const currentHighlightIndex = currentMatch?.messageIndex;

  const [sidebarSelectedIndex, setSidebarSelectedIndex] = useState(0);

  const handleSwitchConversation = useCallback(
    (id: string) => {
      socket.switchConversation(id);
      appMode.setMode('chat');
    },
    [socket, appMode],
  );

  const handleDeleteConversation = useCallback(
    (id: string) => {
      void conversations
        .deleteConversation(id)
        .then((ok) => {
          if (ok && id === socket.conversationId) {
            socket.clearMessages();
          }
        })
        .catch(() => {});
    },
    [conversations, socket],
  );

  useInput((ch, key) => {
    if (key.ctrl && ch === 'c') {
      exit();
    }
    // Ctrl+L: toggle sidebar (refresh on open)
    if (key.ctrl && ch === 'l') {
      const willOpen = !appMode.sidebarOpen;
      appMode.toggleSidebar();
      if (willOpen) {
        void conversations.refresh();
      }
    }
    // Ctrl+N: create new conversation and switch to it
    if (key.ctrl && ch === 'n') {
      void conversations
        .createConversation()
        .then((conv) => {
          if (conv) {
            socket.switchConversation(conv.id);
            appMode.setMode('chat');
          }
        })
        .catch(() => {});
    }
    // Ctrl+K: toggle search mode
    if (key.ctrl && ch === 'k') {
      if (appMode.mode === 'search') {
        search.clear();
        appMode.setMode('chat');
      } else {
        appMode.setMode('search');
      }
    }
    // Page Up / Page Down: scroll message history (only in chat mode)
    if (appMode.mode === 'chat') {
      if (key.pageUp) {
        viewport.scrollBy(-viewport.viewportSize);
      }
      if (key.pageDown) {
        viewport.scrollBy(viewport.viewportSize);
      }
    }
    // Ctrl+T: cycle thinking level
    if (key.ctrl && ch === 't') {
      const levels = socket.availableThinkingLevels;
      if (levels.length > 0) {
        const currentIdx = levels.indexOf(socket.thinkingLevel);
        const nextIdx = (currentIdx + 1) % levels.length;
        const next = levels[nextIdx];
        if (next) {
          socket.setThinkingLevel(next);
        }
      }
    }
    // Escape: return to chat from sidebar/search; in chat, scroll to bottom
    if (key.escape) {
      if (appMode.mode === 'search') {
        search.clear();
        appMode.setMode('chat');
      } else if (appMode.mode === 'sidebar') {
        appMode.setMode('chat');
      } else if (appMode.mode === 'chat') {
        viewport.scrollToBottom();
      }
    }
  });

  const inputPlaceholder =
    appMode.mode === 'sidebar'
      ? 'focus is on sidebar… press Esc to return'
      : appMode.mode === 'search'
        ? 'search mode… press Esc to return'
        : undefined;

  const isSearchMode = appMode.mode === 'search';

  const messageArea = (
    <Box flexDirection="column" flexGrow={1}>
      <MessageList
        messages={socket.messages}
        isStreaming={socket.isStreaming}
        currentStreamText={socket.currentStreamText}
        currentThinkingText={socket.currentThinkingText}
        activeToolCalls={socket.activeToolCalls}
        scrollOffset={viewport.scrollOffset}
        viewportSize={viewport.viewportSize}
        isScrolledUp={viewport.isScrolledUp}
        highlightedMessageIndices={highlightedMessageIndices}
        currentHighlightIndex={currentHighlightIndex}
      />

      {isSearchMode && (
        <SearchBar
          query={search.query}
          onQueryChange={search.setQuery}
          totalMatches={search.totalMatches}
          currentMatch={search.currentMatchIndex}
          onNext={search.nextMatch}
          onPrev={search.prevMatch}
          onClose={() => {
            search.clear();
            appMode.setMode('chat');
          }}
          focused={isSearchMode}
        />
      )}

      <InputBar
        onSubmit={socket.sendMessage}
        isStreaming={socket.isStreaming}
        connected={socket.connected}
        placeholder={inputPlaceholder}
      />
    </Box>
  );

  return (
    <Box flexDirection="column" height="100%">
      <Box marginTop={1} />
      <TopBar
        gatewayUrl={gatewayUrl}
        version="0.0.0"
        modelName={socket.modelName}
        thinkingLevel={socket.thinkingLevel}
        contextWindow={socket.tokenUsage.contextWindow}
        agentName={agentName ?? 'default'}
        connected={socket.connected}
        connecting={socket.connecting}
      />

      {appMode.sidebarOpen ? (
        <Box flexDirection="row" flexGrow={1}>
          <Sidebar
            conversations={conversations.conversations}
            activeConversationId={socket.conversationId}
            selectedIndex={sidebarSelectedIndex}
            onSelectIndex={setSidebarSelectedIndex}
            onSwitchConversation={handleSwitchConversation}
            onDeleteConversation={handleDeleteConversation}
            loading={conversations.loading}
            focused={appMode.mode === 'sidebar'}
            width={30}
          />
          {messageArea}
        </Box>
      ) : (
        <Box flexGrow={1}>{messageArea}</Box>
      )}

      <BottomBar
        gitInfo={gitInfo}
        tokenUsage={socket.tokenUsage}
        connected={socket.connected}
        connecting={socket.connecting}
        modelName={socket.modelName}
        providerName={socket.providerName}
        thinkingLevel={socket.thinkingLevel}
        conversationId={socket.conversationId}
      />
    </Box>
  );
}
