import React, { useState, useCallback, useRef } from 'react';
import { Box, useApp, useInput } from 'ink';
import { StatusBar } from './StatusBar.js';
import { MessageList } from './MessageList.js';
import { InputBar } from './InputBar.js';
import { parseCommand, getHelpText, resolveModelAlias } from '../commands.js';
import type { Message, ModelClient, TokenUsage } from '../../../models/types.js';
import type { ModelRouter } from '../../../models/router.js';
import type { ManagedSession } from '../../../session/index.js';

export interface AppProps {
  session: ManagedSession;
  modelClient: ModelClient;
  modelRouter?: ModelRouter;
  systemPrompt: string;
  model: string;
  onExit?: () => void;
}

export function App({
  session,
  modelClient,
  modelRouter,
  systemPrompt,
  model,
  onExit,
}: AppProps): React.ReactElement {
  const { exit } = useApp();
  const [input, setInput] = useState('');
  const [messages, setMessages] = useState<Message[]>(session.getHistory());
  const [isStreaming, setIsStreaming] = useState(false);
  const [streamingContent, setStreamingContent] = useState('');
  const [scrollOffset, setScrollOffset] = useState(0);
  const [tokenUsage, setTokenUsage] = useState<TokenUsage>({ inputTokens: 0, outputTokens: 0 });
  const [currentModel, setCurrentModel] = useState(model);
  const abortRef = useRef(false);

  useInput((inputChar, key) => {
    if (key.escape) {
      if (isStreaming) {
        abortRef.current = true;
      } else {
        onExit?.();
        exit();
      }
    }

    // Scroll handling
    if (key.upArrow && scrollOffset > 0) {
      setScrollOffset(prev => Math.max(0, prev - 1));
    }
    if (key.downArrow) {
      setScrollOffset(prev => Math.min(messages.length - 1, prev + 1));
    }
    if (key.pageUp) {
      setScrollOffset(prev => Math.max(0, prev - 10));
    }
    if (key.pageDown) {
      setScrollOffset(prev => Math.min(messages.length - 1, prev + 10));
    }
  });

  const handleSubmit = useCallback(async (value: string) => {
    const command = parseCommand(value);
    if (!command) return;

    setInput('');

    // Handle commands
    switch (command.type) {
      case 'quit':
        onExit?.();
        exit();
        return;

      case 'reset':
        session.clear();
        setMessages([]);
        setTokenUsage({ inputTokens: 0, outputTokens: 0 });
        setScrollOffset(0);
        return;

      case 'help':
        // Show help as system message
        setMessages(prev => [...prev, { role: 'assistant', content: getHelpText() }]);
        return;

      case 'status':
        const status = `Session: ${session.id}\nMessages: ${messages.length}\nTokens: ${tokenUsage.inputTokens} in / ${tokenUsage.outputTokens} out`;
        setMessages(prev => [...prev, { role: 'assistant', content: status }]);
        return;

      case 'model':
        if (!modelRouter) {
          setMessages(prev => [...prev, { role: 'assistant', content: 'Model switching not available.' }]);
          return;
        }
        if (!command.name) {
          const info = `Current: ${modelRouter.getTier()}\nAvailable: ${modelRouter.getAvailableTiers().join(', ')}`;
          setMessages(prev => [...prev, { role: 'assistant', content: info }]);
          return;
        }
        const tier = resolveModelAlias(command.name);
        if (modelRouter.setTier(tier)) {
          setCurrentModel(tier);
          setMessages(prev => [...prev, { role: 'assistant', content: `Switched to model: ${tier}` }]);
        } else {
          setMessages(prev => [...prev, { role: 'assistant', content: `Model not available: ${command.name}` }]);
        }
        return;

      case 'fullscreen':
        // Already in fullscreen
        return;

      case 'transfer':
        setMessages(prev => [...prev, { role: 'assistant', content: `Transfer not supported in fullscreen mode.` }]);
        return;

      case 'message':
        break; // Continue to message handling
    }

    if (command.type !== 'message' || isStreaming) return;

    // Add user message
    const userMessage: Message = { role: 'user', content: command.content };
    session.addMessage(userMessage);
    setMessages(prev => [...prev, userMessage]);
    setScrollOffset(0); // Auto-scroll to bottom

    // Stream response
    setIsStreaming(true);
    setStreamingContent('');
    abortRef.current = false;

    try {
      if (modelClient.chatStream) {
        let fullContent = '';

        for await (const event of modelClient.chatStream({
          messages: session.getHistory(),
          system: systemPrompt,
        })) {
          if (abortRef.current) {
            fullContent += '\n\n[interrupted]';
            break;
          }

          if (event.type === 'content' && event.content) {
            fullContent += event.content;
            setStreamingContent(fullContent);
          }
          if (event.type === 'done' && event.usage) {
            setTokenUsage(prev => ({
              inputTokens: prev.inputTokens + event.usage!.inputTokens,
              outputTokens: prev.outputTokens + event.usage!.outputTokens,
            }));
          }
          if (event.type === 'error') {
            throw event.error ?? new Error('Stream error');
          }
        }

        const assistantMessage: Message = { role: 'assistant', content: fullContent };
        session.addMessage(assistantMessage);
        setMessages(prev => [...prev, assistantMessage]);
      } else {
        // Fallback to non-streaming
        const response = await modelClient.chat({
          messages: session.getHistory(),
          system: systemPrompt,
        });

        setTokenUsage(prev => ({
          inputTokens: prev.inputTokens + response.usage.inputTokens,
          outputTokens: prev.outputTokens + response.usage.outputTokens,
        }));

        const assistantMessage: Message = { role: 'assistant', content: response.content };
        session.addMessage(assistantMessage);
        setMessages(prev => [...prev, assistantMessage]);
      }
    } catch (error) {
      const errorMessage: Message = {
        role: 'assistant',
        content: `Error: ${error instanceof Error ? error.message : 'Unknown error'}`,
      };
      setMessages(prev => [...prev, errorMessage]);
    } finally {
      setIsStreaming(false);
      setStreamingContent('');
    }
  }, [isStreaming, session, modelClient, modelRouter, systemPrompt, exit, onExit, messages.length, tokenUsage]);

  return (
    <Box flexDirection="column" height="100%">
      <StatusBar
        sessionId={session.id}
        messageCount={messages.length}
        model={currentModel}
        tokenUsage={{ input: tokenUsage.inputTokens, output: tokenUsage.outputTokens }}
        isStreaming={isStreaming}
      />
      <MessageList
        messages={messages}
        scrollOffset={scrollOffset}
        streamingContent={isStreaming ? streamingContent : undefined}
      />
      <InputBar
        value={input}
        onChange={setInput}
        onSubmit={handleSubmit}
        isLoading={isStreaming}
        placeholder={isStreaming ? 'Flynn is typing... (Esc to cancel)' : 'Type a message... (Esc=exit, /help)'}
      />
    </Box>
  );
}
