<script setup lang="ts">
import { nextTick, ref, onMounted, onUnmounted, watch } from 'vue'
import { Terminal } from 'xterm'
import { AttachAddon } from '@xterm/addon-attach'
import { FitAddon } from '@xterm/addon-fit'
import { useAuthStore } from '../stores/auth'
import 'xterm/css/xterm.css'

const props = defineProps<{
  connectionId: number
  active?: boolean
}>()

const containerRef = ref<HTMLElement | null>(null)
const status = ref<'connecting' | 'connected' | 'error'>('connecting')
const errorMessage = ref('')

let term: Terminal | null = null
let fitAddon: FitAddon | null = null
let ws: WebSocket | null = null
let resizeObserver: ResizeObserver | null = null

function fitTerminal() {
  fitAddon?.fit()
}

function focusTerminal() {
  term?.focus()
}

function refreshTerminalLayout() {
  nextTick(() => {
    fitTerminal()
    focusTerminal()
  })
}

function getWsUrl(): string {
  const authStore = useAuthStore()
  const token = authStore.token
  if (!token) {
    throw new Error('Not authenticated')
  }
  const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'
  const host = window.location.host
  return `${protocol}//${host}/ws/terminal?connectionId=${props.connectionId}&token=${encodeURIComponent(token)}`
}

function cleanup() {
  if (resizeObserver && containerRef.value) {
    resizeObserver.unobserve(containerRef.value)
  }
  resizeObserver = null
  if (ws) {
    ws.close()
    ws = null
  }
  if (term) {
    term.dispose()
    term = null
  }
  fitAddon = null
}

onMounted(() => {
  const authStore = useAuthStore()
  if (!authStore.token) {
    status.value = 'error'
    errorMessage.value = '未登录'
    return
  }

  if (!containerRef.value) return

  term = new Terminal({
    cursorBlink: true,
    theme: {
      background: '#0f172a',
      foreground: '#f1f5f9',
      cursor: '#22d3ee',
      cursorAccent: '#0f172a',
      selectionBackground: 'rgba(34, 211, 238, 0.3)',
    },
    fontSize: 14,
    fontFamily: 'Menlo, Monaco, "Courier New", monospace',
  })

  fitAddon = new FitAddon()
  term.loadAddon(fitAddon)
  term.open(containerRef.value)
  fitTerminal()

  try {
    ws = new WebSocket(getWsUrl())
  } catch (e) {
    status.value = 'error'
    errorMessage.value = e instanceof Error ? e.message : '连接失败'
    return
  }

  ws.onopen = () => {
    status.value = 'connected'
    const attachAddon = new AttachAddon(ws!)
    term!.loadAddon(attachAddon)
    refreshTerminalLayout()
  }

  ws.onerror = () => {
    if (status.value === 'connecting') {
      status.value = 'error'
      errorMessage.value = 'WebSocket 连接失败'
    }
  }

  ws.onclose = () => {
    if (status.value === 'connected') {
      term?.writeln('\r\n[连接已关闭]')
    }
  }

  resizeObserver = new ResizeObserver(() => {
    fitTerminal()
  })
  resizeObserver.observe(containerRef.value)

  if (props.active !== false) {
    refreshTerminalLayout()
  }
})

watch(() => props.active, (active) => {
  if (active) {
    refreshTerminalLayout()
  }
})

onUnmounted(() => {
  cleanup()
})
</script>

<template>
  <div class="h-full flex flex-col bg-slate-950 rounded-lg border border-slate-700 overflow-hidden">
    <div
      ref="containerRef"
      class="flex-1 min-h-0 p-4 xterm-container"
      :class="{ 'flex items-center justify-center': status !== 'connected' }"
      style="min-height: 300px"
    >
      <div v-if="status === 'connecting'" class="text-slate-400">
        正在连接 SSH...
      </div>
      <div v-else-if="status === 'error'" class="text-red-400">
        {{ errorMessage }}
      </div>
    </div>
  </div>
</template>

<style>
.xterm-container .xterm {
  padding: 0.5rem;
}
.xterm-container .xterm-viewport {
  overflow-y: auto !important;
}
</style>
