import React, { useState, useCallback, useEffect, useRef } from 'react';
import { Box, Text, useInput, useApp } from 'ink';
import TextInput from 'ink-text-input';
import Spinner from 'ink-spinner';
import { io, type Socket } from 'socket.io-client';

interface Message {
  role: 'user' | 'assistant';
  content: string;
}

interface TuiAppProps {
  gatewayUrl: string;
  conversationId?: string;
}

export function TuiApp({ gatewayUrl, conversationId: initialConversationId }: TuiAppProps) {
  const { exit } = useApp();
  const [messages, setMessages] = useState<Message[]>([]);
  const [input, setInput] = useState('');
  const [isStreaming, setIsStreaming] = useState(false);
  const [connected, setConnected] = useState(false);
  const [conversationId, setConversationId] = useState(initialConversationId);
  const [currentStreamText, setCurrentStreamText] = useState('');
  const socketRef = useRef<Socket | null>(null);

  useEffect(() => {
    const socket = io(`${gatewayUrl}/chat`, {
      transports: ['websocket'],
    });

    socketRef.current = socket;

    socket.on('connect', () => setConnected(true));
    socket.on('disconnect', () => {
      setConnected(false);
      setIsStreaming(false);
      setCurrentStreamText('');
    });
    socket.on('connect_error', (err: Error) => {
      setMessages((msgs) => [
        ...msgs,
        {
          role: 'assistant',
          content: `Connection failed: ${err.message}. Check that the gateway is running at ${gatewayUrl}.`,
        },
      ]);
    });

    socket.on('message:ack', (data: { conversationId: string }) => {
      setConversationId(data.conversationId);
    });

    socket.on('agent:start', () => {
      setIsStreaming(true);
      setCurrentStreamText('');
    });

    socket.on('agent:text', (data: { text: string }) => {
      setCurrentStreamText((prev) => prev + data.text);
    });

    socket.on('agent:end', () => {
      setCurrentStreamText((prev) => {
        if (prev) {
          setMessages((msgs) => [...msgs, { role: 'assistant', content: prev }]);
        }
        return '';
      });
      setIsStreaming(false);
    });

    socket.on('error', (data: { error: string }) => {
      setMessages((msgs) => [...msgs, { role: 'assistant', content: `Error: ${data.error}` }]);
      setIsStreaming(false);
    });

    return () => {
      socket.disconnect();
    };
  }, [gatewayUrl]);

  const handleSubmit = useCallback(
    (value: string) => {
      if (!value.trim() || isStreaming) return;
      if (!socketRef.current?.connected) {
        setMessages((msgs) => [
          ...msgs,
          { role: 'assistant', content: 'Not connected to gateway. Message not sent.' },
        ]);
        return;
      }

      setMessages((msgs) => [...msgs, { role: 'user', content: value }]);
      setInput('');

      socketRef.current.emit('message', {
        conversationId,
        content: value,
      });
    },
    [conversationId, isStreaming],
  );

  useInput((ch, key) => {
    if (key.ctrl && ch === 'c') {
      exit();
    }
  });

  return (
    <Box flexDirection="column" padding={1}>
      <Box marginBottom={1}>
        <Text bold color="blue">
          Mosaic
        </Text>
        <Text> </Text>
        <Text dimColor>{connected ? `connected` : 'connecting...'}</Text>
        {conversationId && <Text dimColor> | {conversationId.slice(0, 8)}</Text>}
      </Box>

      <Box flexDirection="column" marginBottom={1}>
        {messages.map((msg, i) => (
          <Box key={i} marginBottom={1}>
            <Text bold color={msg.role === 'user' ? 'green' : 'cyan'}>
              {msg.role === 'user' ? '> ' : '  '}
            </Text>
            <Text wrap="wrap">{msg.content}</Text>
          </Box>
        ))}

        {isStreaming && currentStreamText && (
          <Box marginBottom={1}>
            <Text bold color="cyan">
              {'  '}
            </Text>
            <Text wrap="wrap">{currentStreamText}</Text>
          </Box>
        )}

        {isStreaming && !currentStreamText && (
          <Box>
            <Text color="cyan">
              <Spinner type="dots" />
            </Text>
            <Text dimColor> thinking...</Text>
          </Box>
        )}
      </Box>

      <Box>
        <Text bold color="green">
          {'> '}
        </Text>
        <TextInput
          value={input}
          onChange={setInput}
          onSubmit={handleSubmit}
          placeholder={isStreaming ? 'waiting...' : 'type a message'}
        />
      </Box>
    </Box>
  );
}
