<template>
  <AppShell>
    <div class="space-y-3">
      <div class="flex justify-between items-center">
        <h2 class="text-lg font-semibold">终端</h2>
        <div class="flex items-center gap-3">
          <NTag :type="statusType" size="small" round>
            {{ statusLabel }}
          </NTag>
          <NButton
            size="small"
            :disabled="sock.status.value === 'connecting' || sock.status.value === 'open'"
            @click="sock.reconnect()"
          >
            重连
          </NButton>
        </div>
      </div>

      <NAlert type="warning" :show-icon="true" title="管理员终端">
        命令在 server 宿主进程（部署时即 runtime 容器）内执行，权限等同容器内 shell。 安装 ACP
        客户端示例：<code>npm i -g @anthropic-ai/claude-code</code>， 安装完成后将
        <code>binary_path</code> 填入对应的 ACP 客户端配置。
      </NAlert>

      <div ref="containerEl" class="terminal-host" />
    </div>
  </AppShell>
</template>

<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { NAlert, NButton, NTag } from 'naive-ui'
import { Terminal } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import '@xterm/xterm/css/xterm.css'
import AppShell from '@/layouts/AppShell.vue'
import { useTerminalSocket } from '@/composables/useTerminalSocket'

const containerEl = ref<HTMLElement | null>(null)

const term = new Terminal({
  fontFamily: 'Menlo, Consolas, "DejaVu Sans Mono", monospace',
  fontSize: 13,
  cursorBlink: true,
  convertEol: false,
  scrollback: 5000,
})
const fitAddon = new FitAddon()
term.loadAddon(fitAddon)

// 在 setup 同步阶段建立连接（onScopeDispose 才能正确挂到组件 scope）。
// term.write 在 open() 前会被 xterm 内部缓冲，待挂载后渲染。
const sock = useTerminalSocket({
  onData: (text) => term.write(text),
  initialRows: term.rows,
  initialCols: term.cols,
})
term.onData((d: string) => sock.sendInput(d))

let resizeObserver: ResizeObserver | null = null

function fitAndReport() {
  if (!containerEl.value) return
  try {
    fitAddon.fit()
  } catch {
    return
  }
  sock.sendResize(term.rows, term.cols)
}

onMounted(() => {
  if (!containerEl.value) return
  term.open(containerEl.value)
  fitAndReport()
  resizeObserver = new ResizeObserver(() => fitAndReport())
  resizeObserver.observe(containerEl.value)
  term.focus()
})

onBeforeUnmount(() => {
  resizeObserver?.disconnect()
  resizeObserver = null
  sock.close()
  term.dispose()
})

const statusLabel = computed(() => {
  switch (sock.status.value) {
    case 'connecting':
      return '连接中'
    case 'open':
      return '已连接'
    case 'error':
      return '错误'
    case 'closed':
      return '已断开'
    default:
      return '空闲'
  }
})

const statusType = computed<'success' | 'warning' | 'error' | 'default'>(() => {
  switch (sock.status.value) {
    case 'open':
      return 'success'
    case 'connecting':
      return 'warning'
    case 'error':
      return 'error'
    default:
      return 'default'
  }
})
</script>

<style scoped>
.terminal-host {
  width: 100%;
  height: calc(100vh - 220px);
  min-height: 320px;
  background: #000;
  padding: 8px;
  border-radius: 6px;
  overflow: hidden;
}
</style>
