<template>
  <div class="terminal-emulator" ref="terminalContainer">
    <div class="terminal-emulator__output" ref="outputContainer">
      <div v-for="(line, index) in terminalStore.output" :key="index" class="terminal-line">
        <span v-html="ansiToHtml(line)"></span>
      </div>
      <div v-if="terminalStore.output.length === 0" class="terminal-emulator__welcome">
        Fischer AgentKit 智能终端 - 输入命令开始
      </div>
    </div>
    <div class="terminal-emulator__input">
      <span class="terminal-emulator__prompt">{{ terminalStore.cwd }}$</span>
      <input
        ref="inputRef"
        v-model="commandInput"
        class="terminal-emulator__input-field"
        placeholder="输入命令..."
        :disabled="terminalStore.isExecuting"
        @keydown.enter="executeCommand"
        @keydown.up.prevent="historyUp"
        @keydown.down.prevent="historyDown"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, nextTick, onMounted } from 'vue'
import { useTerminalStore } from '@/stores/terminal'

const terminalStore = useTerminalStore()
const commandInput = ref('')
const inputRef = ref<HTMLInputElement | null>(null)
const outputContainer = ref<HTMLElement | null>(null)
const historyIndex = ref(-1)

onMounted(() => {
  terminalStore.connectWebSocket()
})

// Auto-scroll to bottom
watch(
  () => terminalStore.output.length,
  async () => {
    await nextTick()
    if (outputContainer.value) {
      outputContainer.value.scrollTop = outputContainer.value.scrollHeight
    }
  }
)

function executeCommand(): void {
  const cmd = commandInput.value.trim()
  if (!cmd) return
  terminalStore.sendCommand(cmd)
  commandInput.value = ''
  historyIndex.value = -1
}

function historyUp(): void {
  const history = terminalStore.history
  if (history.length === 0) return
  if (historyIndex.value < history.length - 1) {
    historyIndex.value++
    commandInput.value = history[history.length - 1 - historyIndex.value].command
  }
}

function historyDown(): void {
  if (historyIndex.value > 0) {
    historyIndex.value--
    commandInput.value = terminalStore.history[terminalStore.history.length - 1 - historyIndex.value].command
  } else {
    historyIndex.value = -1
    commandInput.value = ''
  }
}

function ansiToHtml(text: string): string {
  // Basic ANSI color code conversion using One Dark Pro palette
  // Track open spans to ensure balanced HTML on reset
  let html = text
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;')

  // Replace color codes with close-previous + open-new pattern
  // and track span depth for balanced closing on reset
  let spanDepth = 0
  const result: string[] = []
  let i = 0

  while (i < html.length) {
    // Check for ANSI escape sequence
    if (html[i] === '\x1b' && i + 1 < html.length && html[i + 1] === '[') {
      const endIdx = html.indexOf('m', i + 2)
      if (endIdx !== -1) {
        const code = html.substring(i + 2, endIdx)
        if (code === '0' || code === '') {
          // Reset: close all open spans
          for (let s = 0; s < spanDepth; s++) {
            result.push('</span>')
          }
          spanDepth = 0
        } else {
          // Color code: close previous span if any, open new one
          const colorMap: Record<string, string> = {
            '32': 'ansi-green',
            '33': 'ansi-yellow',
            '31': 'ansi-red',
            '36': 'ansi-cyan',
            '34': 'ansi-blue',
            '35': 'ansi-magenta',
            '1': 'ansi-bold',
          }
          const cls = colorMap[code]
          if (cls) {
            result.push(`<span class="${cls}">`)
            spanDepth++
          }
        }
        i = endIdx + 1
      } else {
        result.push(html[i])
        i++
      }
    } else {
      result.push(html[i])
      i++
    }
  }

  // Close any remaining open spans
  for (let s = 0; s < spanDepth; s++) {
    result.push('</span>')
  }

  return result.join('')
}
</script>

<style scoped>
.terminal-emulator {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--code-bg);
  border-radius: var(--radius-md);
  overflow: hidden;
  font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', Menlo, Consolas, monospace;
}

.terminal-emulator__output {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-3);
  font-size: var(--font-sm);
  line-height: var(--leading-normal);
  color: var(--code-fg);
}

.terminal-emulator__welcome {
  color: var(--code-comment);
  font-style: italic;
}

.terminal-emulator__input {
  display: flex;
  align-items: center;
  padding: var(--space-2) var(--space-3);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(0, 0, 0, 0.2);
}

.terminal-emulator__prompt {
  color: var(--code-string);
  margin-right: var(--space-2);
  font-size: var(--font-sm);
  white-space: nowrap;
}

.terminal-emulator__input-field {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--code-fg);
  font-family: inherit;
  font-size: var(--font-sm);
}

.terminal-emulator__input-field::placeholder {
  color: var(--code-comment);
}

.terminal-line {
  white-space: pre-wrap;
  word-break: break-all;
}

/* ANSI color classes using One Dark Pro palette */
.terminal-line :deep(.ansi-green) { color: var(--code-string); }
.terminal-line :deep(.ansi-yellow) { color: var(--code-number); }
.terminal-line :deep(.ansi-red) { color: var(--code-variable); }
.terminal-line :deep(.ansi-cyan) { color: var(--code-function); }
.terminal-line :deep(.ansi-blue) { color: var(--code-function); }
.terminal-line :deep(.ansi-magenta) { color: var(--code-keyword); }
</style>
