import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {render, Box, Text, useApp, useInput} from 'ink';

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const nowTime = () => new Date().toLocaleTimeString('zh-CN', {hour12: false});

function Header({running, turn}) {
  return (
    <Box flexDirection="column" marginBottom={1}>
      <Text color="cyan" bold>Agents CLI React/Ink Demo</Text>
      <Text color="gray">
        React 组件渲染到终端 · 支持粘贴多行 · 输入后回车发送 · Ctrl+C 取消当前任务 · /help 或 /exit
      </Text>
      <Box gap={2}>
        <Text>状态: <Text color={running ? 'yellow' : 'green'}>{running ? '运行中' : '空闲'}</Text></Text>
        <Text>Turn: <Text color="magenta">#{turn}</Text></Text>
      </Box>
    </Box>
  );
}

function Message({item}) {
  if (item.type === 'user') {
    return (
      <Box marginTop={1}>
        <Text color="blue" bold>你</Text>
        <Text color="gray"> {item.time} </Text>
        <Text>{item.text}</Text>
      </Box>
    );
  }

  if (item.type === 'assistant') {
    return (
      <Box marginTop={1} flexDirection="column">
        <Text color="green" bold>助手 <Text color="gray">{item.time}</Text></Text>
        <Text>{item.text || <Text color="gray">正在生成...</Text>}</Text>
      </Box>
    );
  }

  if (item.type === 'tool') {
    const statusColor = item.status === 'done' ? 'green' : item.status === 'running' ? 'yellow' : 'red';
    return (
      <Box marginTop={1} borderStyle="round" borderColor={statusColor} paddingX={1} flexDirection="column">
        <Text color={statusColor} bold>{item.status === 'done' ? '✓' : '●'} 工具调用：{item.name}</Text>
        <Text color="gray">{item.detail}</Text>
        {item.output ? <Text>{item.output}</Text> : null}
      </Box>
    );
  }

  if (item.type === 'system') {
    return <Text color="gray">◆ {item.text}</Text>;
  }

  return null;
}

function Transcript({items}) {
  const visible = items.slice(-10);
  return (
    <Box flexDirection="column" minHeight={12}>
      {visible.map(item => <Message key={item.id} item={item} />)}
    </Box>
  );
}

function Composer({value, running}) {
  return (
    <Box flexDirection="column" marginTop={1}>
      <Box borderStyle="single" borderColor={running ? 'yellow' : 'cyan'} paddingX={1}>
        <Text color="cyan">› </Text>
        <Text>{value}</Text>
        <Text inverse> </Text>
      </Box>
      <Text color="gray">快捷键：Enter 发送 · 直接粘贴可保留多行 · Backspace 删除 · Ctrl+C {running ? '取消任务' : '退出'} · /help 查看命令</Text>
    </Box>
  );
}

function App() {
  const {exit} = useApp();
  const [input, setInput] = useState('');
  const [items, setItems] = useState(() => [
    {id: 'welcome', type: 'system', text: '这是一个本地 React/Ink CLI demo：消息区、工具卡片、输入框和状态栏都是 React 组件。'}
  ]);
  const [running, setRunning] = useState(false);
  const [turn, setTurn] = useState(0);
  const cancelRef = useRef(false);
  const nextId = useRef(1);

  const append = useCallback(item => {
    setItems(prev => [...prev, {id: String(nextId.current++), ...item}]);
  }, []);

  const patchLastAssistant = useCallback(delta => {
    setItems(prev => {
      const copy = [...prev];
      for (let i = copy.length - 1; i >= 0; i--) {
        if (copy[i].type === 'assistant') {
          copy[i] = {...copy[i], text: (copy[i].text || '') + delta};
          break;
        }
      }
      return copy;
    });
  }, []);

  const markLastToolDone = useCallback(output => {
    setItems(prev => {
      const copy = [...prev];
      for (let i = copy.length - 1; i >= 0; i--) {
        if (copy[i].type === 'tool' && copy[i].status === 'running') {
          copy[i] = {...copy[i], status: 'done', output};
          break;
        }
      }
      return copy;
    });
  }, []);

  const runFakeAgent = useCallback(async prompt => {
    setRunning(true);
    cancelRef.current = false;
    const currentTurn = turn + 1;
    setTurn(currentTurn);

    append({type: 'user', text: prompt, time: nowTime()});

    if (prompt === '/help') {
      append({type: 'assistant', time: nowTime(), text: '可用命令：/help、/clear、/exit。普通文本会触发一段模拟 Agent 流程。现在可以直接粘贴多行内容，换行会保留在输入框和提交内容中。'});
      setRunning(false);
      return;
    }
    if (prompt === '/clear') {
      setItems([{id: 'cleared', type: 'system', text: '已清屏。'}]);
      setRunning(false);
      return;
    }
    if (prompt === '/exit') {
      exit();
      return;
    }

    append({type: 'assistant', time: nowTime(), text: ''});
    const sentence = '我会先读取项目结构，然后模拟一次工具调用，最后给出结论。这些内容是流式追加的，不会破坏下方输入框。';
    for (const char of sentence) {
      if (cancelRef.current) {
        patchLastAssistant('\n\n[已取消]');
        setRunning(false);
        return;
      }
      patchLastAssistant(char);
      await sleep(28);
    }

    append({type: 'tool', status: 'running', name: 'list_files', detail: '扫描当前 workspace 的关键文件...'});
    await sleep(900);
    if (cancelRef.current) {
      markLastToolDone('任务取消，工具调用未继续。');
      setRunning(false);
      return;
    }
    markLastToolDone('发现 main.py、server/tasks.py、core/main_terminal.py。');

    append({type: 'assistant', time: nowTime(), text: ''});
    const finalText = '结论：CLI 可以像一个终端 App 一样工作。后续只需要把这里的模拟事件替换成 /api/tasks 的真实事件流。';
    for (const char of finalText) {
      if (cancelRef.current) {
        patchLastAssistant('\n\n[已取消]');
        setRunning(false);
        return;
      }
      patchLastAssistant(char);
      await sleep(24);
    }
    setRunning(false);
  }, [append, exit, markLastToolDone, patchLastAssistant, turn]);

  useInput((chunk, key) => {
    if (key.ctrl && chunk === 'c') {
      if (running) {
        cancelRef.current = true;
        append({type: 'system', text: '收到 Ctrl+C：正在取消当前模拟任务...'});
      } else {
        exit();
      }
      return;
    }

    if (running) {
      return;
    }

    if (key.return) {
      const text = input.trim();
      if (!text) return;
      setInput('');
      void runFakeAgent(text);
      return;
    }

    if (key.backspace || key.delete) {
      setInput(prev => Array.from(prev).slice(0, -1).join(''));
      return;
    }

    if (chunk && !key.ctrl && !key.meta) {
      // Ink 在 bracketed paste 或快速粘贴时，可能一次性给到包含换行的 chunk。
      // 这里把 CRLF/CR 统一成 LF，并保留多行内容；单独按 Enter 的提交逻辑在上面处理。
      const normalized = chunk.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
      setInput(prev => prev + normalized);
    }
  });

  return (
    <Box flexDirection="column" paddingX={1}>
      <Header running={running} turn={turn} />
      <Transcript items={items} />
      <Composer value={input} running={running} />
    </Box>
  );
}

render(<App />);
