<template>
  <v-container
    fluid
    class="pa-0 d-flex flex-column terminal-root"
    :class="theme.global.current.value.dark ? 'terminal-root--dark' : 'terminal-root--light'"
  >
    <v-menu
      v-model="showTermMenu"
      :target="[menuX, menuY]"
      :location="termMenuLocation"
      content-class="terminal-ctx-menu"
    >
      <v-list density="comfortable" nav class="py-1">
        <v-list-item prepend-icon="mdi-content-copy" rounded="lg" @click="termCopy">
          <v-list-item-title>复制</v-list-item-title>
        </v-list-item>
        <v-list-item prepend-icon="mdi-content-paste" rounded="lg" @click="termPaste">
          <v-list-item-title>粘贴</v-list-item-title>
        </v-list-item>
        <v-list-item prepend-icon="mdi-select-all" rounded="lg" @click="termSelectAll">
          <v-list-item-title>全选</v-list-item-title>
        </v-list-item>
        <template v-if="termMenuContext === 'terminal'">
          <v-divider class="my-1" />
          <v-list-item prepend-icon="mdi-delete-sweep" rounded="lg" @click="termClear">
            <v-list-item-title>清屏</v-list-item-title>
          </v-list-item>
          <v-list-item
            prepend-icon="mdi-logout"
            rounded="lg"
            :disabled="!activeSession"
            @click="termDisconnect"
          >
            <v-list-item-title class="text-error">断开连接</v-list-item-title>
          </v-list-item>
        </template>
      </v-list>
    </v-menu>

    <div class="d-flex flex-column flex-grow-1" style="min-height: 0">
      <TerminalTabBar
        :sessions="sessions"
        :active-session-id="activeSessionId"
        @switch="switchTab"
        @close="closeTab"
        @new="newSession()"
      />

      <TerminalToolbar
        v-if="sessions.length > 0"
        :session="activeSession"
        :font-size="fontSize"
        :scrollback="scrollback"
        @font-change="changeFontSize"
        @scrollback-change="changeScrollback"
        @fullscreen="toggleFullscreen"
        @disconnect="disconnect"
      />

      <TerminalArea
        ref="areaRef"
        :sessions="sessions"
        :active-session-id="activeSessionId"
        :active-session="activeSession"
        :show-empty="showEmptyPicker"
        @context-menu="onTermContext"
        @pane-ref="setTermRef"
        @reconnect="reconnect"
        @go-servers="router.push('/servers')"
      >
        <template #empty-picker>
          <TerminalServerPicker
            :servers="filteredServers"
            :search="serverSearch"
            mode="inline"
            @update:search="onServerSearch"
            @select="(id) => newSession(id)"
          />
        </template>
      </TerminalArea>

      <TerminalQuickBar :commands="quickCmds" @exec="execQuickCmd" />

      <TerminalCmdBar
        ref="cmdBarRef"
        v-model="cmdInput"
        :disabled="!activeSessionConnected"
        @keydown="onCmdKeydown"
        @send="sendCmd"
        @ctrl="sendCtrl"
        @key="sendKey"
        @context-menu="onCmdContext"
      />
    </div>

    <v-dialog v-model="showServerDialog" max-width="400">
      <v-card border>
        <v-card-title>选择服务器</v-card-title>
        <v-card-text>
          <TerminalServerPicker
            :servers="filteredServers"
            :search="serverSearch"
            mode="dialog"
            @update:search="onServerSearch"
            @select="onDialogSelect"
          />
        </v-card-text>
      </v-card>
    </v-dialog>
  </v-container>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch, inject, type Ref } from 'vue'
import { useRouter } from 'vue-router'
import { useTheme } from 'vuetify'
import '@xterm/xterm/css/xterm.css'
import { useSnackbar } from '@/composables/useSnackbar'
import { useTerminalSessions } from '@/composables/terminal/useTerminalSessions'
import TerminalTabBar from '@/components/terminal/TerminalTabBar.vue'
import TerminalToolbar from '@/components/terminal/TerminalToolbar.vue'
import TerminalArea from '@/components/terminal/TerminalArea.vue'
import TerminalCmdBar from '@/components/terminal/TerminalCmdBar.vue'
import TerminalQuickBar from '@/components/terminal/TerminalQuickBar.vue'
import TerminalServerPicker from '@/components/terminal/TerminalServerPicker.vue'

const router = useRouter()
const theme = useTheme()
const snackbar = useSnackbar()
const nexusDrawer = inject<Ref<boolean>>('nexusDrawer', ref(true))

const {
  sessions,
  activeSessionId,
  activeSession,
  activeSessionConnected,
  termContainer,
  showServerDialog,
  serverSearch,
  filteredServers,
  showEmptyPicker,
  quickCmds,
  fontSize,
  scrollback,
  changeFontSize,
  changeScrollback,
  cmdInput,
  sendCmd,
  onCmdKeydown,
  sendCtrl,
  sendKey,
  setTermRef,
  switchTab,
  closeTab,
  newSession,
  disconnect,
  reconnect,
  toggleFullscreen,
  loadQuickCmds,
  execQuickCmd,
  loadServers,
  applyRouteQuery,
  onGlobalKeydown,
  disposeAllSessions,
  getNative,
} = useTerminalSessions(nexusDrawer)

const areaRef = ref<InstanceType<typeof TerminalArea> | null>(null)
const cmdBarRef = ref<InstanceType<typeof TerminalCmdBar> | null>(null)

watch(
  () => areaRef.value?.containerRef,
  (el) => {
    termContainer.value = el ?? null
  },
  { flush: 'post' },
)

const showTermMenu = ref(false)
const menuX = ref(0)
const menuY = ref(0)
const termMenuContext = ref<'terminal' | 'command'>('terminal')
const termMenuLocation = computed(() =>
  menuY.value > window.innerHeight * 0.55 ? 'top end' : 'bottom end',
)

function onServerSearch(v: string) {
  serverSearch.value = v
}

function openTermMenu(e: MouseEvent, context: 'terminal' | 'command') {
  e.preventDefault()
  termMenuContext.value = context
  menuX.value = e.clientX
  menuY.value = e.clientY
  showTermMenu.value = true
}

function onTermContext(e: MouseEvent) {
  openTermMenu(e, 'terminal')
}

function onCmdContext(e: MouseEvent) {
  openTermMenu(e, 'command')
}

function onDialogSelect(serverId: number) {
  showServerDialog.value = false
  void newSession(serverId)
}

function termCopy() {
  showTermMenu.value = false
  if (termMenuContext.value === 'command') {
    const text = cmdBarRef.value?.getSelectionText() || ''
    if (text) {
      navigator.clipboard.writeText(text).catch(() => snackbar('复制失败', 'error'))
      return
    }
  }
  const sel = window.getSelection()?.toString()
  if (sel) {
    navigator.clipboard.writeText(sel).catch(() => snackbar('复制失败', 'error'))
    return
  }
  const s = activeSession.value
  if (s) {
    const buffer = getNative(s.id)?.term?.getSelection()
    if (buffer) navigator.clipboard.writeText(buffer).catch(() => snackbar('复制失败', 'error'))
  }
}

async function termPaste() {
  showTermMenu.value = false
  try {
    const text = await navigator.clipboard.readText()
    if (!text) return
    if (termMenuContext.value === 'command') {
      await cmdBarRef.value?.insertAtSelection(text)
      return
    }
    const s = activeSession.value
    getNative(s?.id || '')?.term?.paste(text)
  } catch {
    snackbar('剪贴板访问被拒绝', 'warning')
  }
}

function termSelectAll() {
  showTermMenu.value = false
  if (termMenuContext.value === 'command') {
    cmdBarRef.value?.selectAllInput()
    return
  }
  getNative(activeSession.value?.id || '')?.term?.selectAll()
}

function termClear() {
  showTermMenu.value = false
  getNative(activeSession.value?.id || '')?.term?.clear()
}

function termDisconnect() {
  showTermMenu.value = false
  const s = activeSession.value
  if (s) void closeTab(s.id)
}

onMounted(async () => {
  document.addEventListener('keydown', onGlobalKeydown)
  await loadServers()
  await loadQuickCmds()
  await applyRouteQuery()
})

onBeforeUnmount(() => {
  document.removeEventListener('keydown', onGlobalKeydown)
  disposeAllSessions()
})
</script>

<style scoped>
.terminal-root {
  flex: 1 1 auto;
  min-height: 0;
  min-width: 0;
  width: 100%;
  max-width: 100%;
  height: 100%;
  box-sizing: border-box;
}
.terminal-root--dark {
  background: #0b1120;
}
.terminal-root--light {
  background: rgb(var(--v-theme-background));
}
.font-mono {
  font-family: 'Cascadia Code', 'Fira Code', 'JetBrains Mono', Menlo, monospace;
}
:deep(.xterm) {
  /* 顶部不加 padding，避免首行字符被 overflow 裁切 */
  padding: 0 4px 4px;
  height: 100%;
  box-sizing: border-box;
}
:deep(.xterm-viewport) {
  overflow-y: auto !important;
}
:deep(.terminal-ctx-menu) {
  min-width: 11.5rem;
}
:deep(.terminal-ctx-menu .v-list-item-title) {
  font-size: 1rem;
  font-weight: 500;
}
:deep(.terminal-ctx-menu .v-icon) {
  font-size: 1.35rem;
}
.terminal-root--dark :deep(.sh-keyword) {
  color: #60a5fa;
  font-weight: 500;
}
.terminal-root--dark :deep(.sh-builtin) {
  color: #38bdf8;
}
.terminal-root--dark :deep(.sh-sudo) {
  color: #f87171;
  font-weight: 600;
}
.terminal-root--dark :deep(.sh-pipe),
.terminal-root--dark :deep(.sh-operator) {
  color: #fbbf24;
  font-weight: 600;
}
.terminal-root--dark :deep(.sh-redirect) {
  color: #fb923c;
}
.terminal-root--dark :deep(.sh-string) {
  color: #4ade80;
}
.terminal-root--dark :deep(.sh-flag) {
  color: #c084fc;
}
.terminal-root--dark :deep(.sh-variable) {
  color: #f472b6;
}
.terminal-root--dark :deep(.sh-backtick) {
  color: #a78bfa;
}
.terminal-root--dark :deep(.sh-comment) {
  color: #64748b;
  font-style: italic;
}
.terminal-root--dark :deep(.sh-number) {
  color: #fbbf24;
}
.terminal-root--dark :deep(.sh-path) {
  color: #2dd4bf;
}
.terminal-root--dark :deep(.sh-text) {
  color: #e2e8f0;
}
.terminal-root--light :deep(.sh-keyword) {
  color: #2563eb;
  font-weight: 500;
}
.terminal-root--light :deep(.sh-builtin) {
  color: #0284c7;
}
.terminal-root--light :deep(.sh-sudo) {
  color: #dc2626;
  font-weight: 600;
}
.terminal-root--light :deep(.sh-pipe),
.terminal-root--light :deep(.sh-operator) {
  color: #ca8a04;
  font-weight: 600;
}
.terminal-root--light :deep(.sh-redirect) {
  color: #ea580c;
}
.terminal-root--light :deep(.sh-string) {
  color: #16a34a;
}
.terminal-root--light :deep(.sh-flag) {
  color: #9333ea;
}
.terminal-root--light :deep(.sh-variable) {
  color: #db2777;
}
.terminal-root--light :deep(.sh-backtick) {
  color: #7c3aed;
}
.terminal-root--light :deep(.sh-comment) {
  color: #94a3b8;
  font-style: italic;
}
.terminal-root--light :deep(.sh-number) {
  color: #d97706;
}
.terminal-root--light :deep(.sh-path) {
  color: #0d9488;
}
.terminal-root--light :deep(.sh-text) {
  color: #0f172a;
}
</style>
