<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount, nextTick, watch, computed } from 'vue'
import { connectWithToken, stopTokenPolling } from '../services/webmcp'
import { useCanvasStore } from '../stores/canvas'
import { endpoints } from '../config/endpoints'
import { useTerminalRenderer } from '../composables/useTerminalRenderer'

const props = defineProps<{
  modelValue: boolean
}>()

const emit = defineEmits<{
  'update:modelValue': [value: boolean]
}>()

const isOpen = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})

const canvasStore = useCanvasStore()

// ============================================================================
// REFS
// ============================================================================

const terminalContainer = ref<HTMLElement | null>(null)
const terminalRef = ref<HTMLElement | null>(null)
const connected = ref(false)
const connecting = ref(false)
const sessionId = ref<string | null>(null)

// Drag state
const isDragging = ref(false)
const position = ref({ x: 0, y: 0 })
const hasCustomPosition = ref(false)
const dragOffset = ref({ x: 0, y: 0 })

// Resize state
const isResizing = ref(false)
const size = ref({ w: 580, h: 360 })

// Mobile bottom sheet state
const isMobile = ref(false)
const sheetHeight = ref(55)
const isDraggingSheet = ref(false)
const sheetDragStart = ref({ y: 0, height: 0 })
const keyboardHeight = ref(0)
const snapPoints = [20, 55, 85]

// WebSocket
let socket: WebSocket | null = null
let reconnectTimeout: number | null = null
let reconnectAttempts = 0
const MAX_RECONNECT_ATTEMPTS = 10
const RECONNECT_DELAY_MS = 2000

// Token detection
let tokenBuffer = ''
let tokenTimeout: number | null = null
const waitingForToken = ref(false)

const WS_URL = endpoints.terminal

// Mouse tracking for Ctrl+E
const mousePos = ref({ x: 0, y: 0 })
let lastToggle = 0

// ============================================================================
// TERMINAL RENDERER (composable)
// ============================================================================

const renderer = useTerminalRenderer({
  container: terminalContainer,
  onData: (data) => {
    // Send user input to server
    if (socket && socket.readyState === WebSocket.OPEN) {
      socket.send(JSON.stringify({ type: 'input', data }))
    }
  },
  onResize: (cols, rows) => {
    // Notify server of resize
    if (socket && socket.readyState === WebSocket.OPEN) {
      socket.send(JSON.stringify({ type: 'resize', cols, rows }))
    }
  },
  onKeyEvent: (e) => {
    // Ctrl+E: Toggle terminal
    if (e.ctrlKey && e.key === 'e') {
      e.preventDefault()
      toggleTerminal()
      return false
    }

    // Ctrl+V: Paste from clipboard
    if (e.ctrlKey && e.key === 'v' && e.type === 'keydown') {
      e.preventDefault()
      navigator.clipboard.readText().then((text) => {
        if (text && socket && socket.readyState === WebSocket.OPEN) {
          socket.send(JSON.stringify({ type: 'input', data: text }))
        }
      }).catch(console.error)
      return false
    }

    // Ctrl+C: Copy selection (if any)
    if (e.ctrlKey && e.key === 'c' && e.type === 'keydown') {
      const selection = renderer.getSelection()
      if (selection) {
        navigator.clipboard.writeText(selection).catch(console.error)
        return false
      }
    }

    return true
  }
})

// ============================================================================
// MOBILE DETECTION
// ============================================================================

function checkMobile() {
  const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0
  const isSmallScreen = window.innerWidth <= 1024
  isMobile.value = isTouchDevice && isSmallScreen
}

function setupKeyboardDetection() {
  if (window.visualViewport) {
    window.visualViewport.addEventListener('resize', handleViewportResize)
  }
}

function handleViewportResize() {
  if (!window.visualViewport || !isMobile.value) return

  const viewportHeight = window.visualViewport.height
  const windowHeight = window.innerHeight
  const diff = windowHeight - viewportHeight

  if (diff > 100) {
    keyboardHeight.value = diff
    if (sheetHeight.value < 55 && isOpen.value) {
      sheetHeight.value = 55
    }
  } else {
    keyboardHeight.value = 0
  }
}

function findNearestSnap(height: number): number {
  return snapPoints.reduce((prev, curr) =>
    Math.abs(curr - height) < Math.abs(prev - height) ? curr : prev
  )
}

// ============================================================================
// MOBILE SHEET DRAG
// ============================================================================

function startSheetDrag(e: TouchEvent) {
  if (!isMobile.value) return
  const touch = e.touches[0]
  if (!touch) return

  isDraggingSheet.value = true
  sheetDragStart.value = { y: touch.clientY, height: sheetHeight.value }
}

function onSheetDrag(e: TouchEvent) {
  if (!isDraggingSheet.value || !isMobile.value) return
  const touch = e.touches[0]
  if (!touch) return

  const deltaY = sheetDragStart.value.y - touch.clientY
  const deltaPercent = (deltaY / window.innerHeight) * 100
  sheetHeight.value = Math.max(15, Math.min(90, sheetDragStart.value.height + deltaPercent))
}

function stopSheetDrag() {
  if (!isDraggingSheet.value) return
  isDraggingSheet.value = false
  sheetHeight.value = findNearestSnap(sheetHeight.value)
  nextTick(() => renderer.fit())
}

// ============================================================================
// WINDOW DRAG
// ============================================================================

function trackMouse(e: MouseEvent) {
  mousePos.value = { x: e.clientX, y: e.clientY }
}

function toggleTerminal() {
  const now = Date.now()
  if (now - lastToggle < 150) return
  lastToggle = now

  if (!isOpen.value) {
    const w = size.value.w
    const h = size.value.h
    const minX = -w * 0.75
    const maxX = window.innerWidth - w * 0.25
    const minY = -h * 0.75
    const maxY = window.innerHeight - h * 0.25
    position.value = {
      x: Math.max(minX, Math.min(mousePos.value.x - w / 2, maxX)),
      y: Math.max(minY, Math.min(mousePos.value.y - h / 2, maxY))
    }
    hasCustomPosition.value = true
    isOpen.value = true
  } else {
    isOpen.value = false
  }
}

function handleKeydown(e: KeyboardEvent) {
  if (e.ctrlKey && e.key === 'e') {
    e.preventDefault()
    toggleTerminal()
  }
}

function startDrag(e: MouseEvent | TouchEvent) {
  if ((e.target as HTMLElement).closest('.window-controls')) return

  if (isMobile.value) {
    if (e instanceof TouchEvent) startSheetDrag(e)
    return
  }

  isDragging.value = true
  const rect = terminalRef.value?.getBoundingClientRect()
  const touch = e instanceof TouchEvent ? e.touches[0] : null
  const clientX = e instanceof MouseEvent ? e.clientX : (touch?.clientX ?? 0)
  const clientY = e instanceof MouseEvent ? e.clientY : (touch?.clientY ?? 0)

  if (rect) {
    if (!hasCustomPosition.value) {
      position.value = { x: rect.left, y: rect.top }
    }
    dragOffset.value = { x: clientX - rect.left, y: clientY - rect.top }
  }

  document.addEventListener('mousemove', onDrag)
  document.addEventListener('mouseup', stopDrag)
  document.addEventListener('touchmove', onDrag, { passive: false })
  document.addEventListener('touchend', stopDrag)
}

function onDrag(e: MouseEvent | TouchEvent) {
  if (!isDragging.value) return
  if (e instanceof TouchEvent) e.preventDefault()

  const touch = e instanceof TouchEvent ? e.touches[0] : null
  const clientX = e instanceof MouseEvent ? e.clientX : (touch?.clientX ?? 0)
  const clientY = e instanceof MouseEvent ? e.clientY : (touch?.clientY ?? 0)

  const w = terminalRef.value?.offsetWidth || 580
  const h = terminalRef.value?.offsetHeight || 360
  const minX = -w * 0.75
  const maxX = window.innerWidth - w * 0.25
  const minY = -h * 0.75
  const maxY = window.innerHeight - h * 0.25

  position.value = {
    x: Math.max(minX, Math.min(clientX - dragOffset.value.x, maxX)),
    y: Math.max(minY, Math.min(clientY - dragOffset.value.y, maxY))
  }
}

function stopDrag() {
  isDragging.value = false
  hasCustomPosition.value = true
  document.removeEventListener('mousemove', onDrag)
  document.removeEventListener('mouseup', stopDrag)
  document.removeEventListener('touchmove', onDrag)
  document.removeEventListener('touchend', stopDrag)
}

// ============================================================================
// WINDOW RESIZE
// ============================================================================

const resizeStart = ref({ x: 0, y: 0, w: 0, h: 0 })

function startResize(e: MouseEvent) {
  e.preventDefault()
  e.stopPropagation()
  isResizing.value = true
  resizeStart.value = { x: e.clientX, y: e.clientY, w: size.value.w, h: size.value.h }
  document.addEventListener('mousemove', onResize)
  document.addEventListener('mouseup', stopResize)
}

function onResize(e: MouseEvent) {
  if (!isResizing.value) return
  size.value = {
    w: Math.max(400, Math.min(resizeStart.value.w + e.clientX - resizeStart.value.x, window.innerWidth - 40)),
    h: Math.max(250, Math.min(resizeStart.value.h + e.clientY - resizeStart.value.y, window.innerHeight - 40))
  }
}

function stopResize() {
  isResizing.value = false
  document.removeEventListener('mousemove', onResize)
  document.removeEventListener('mouseup', stopResize)
  nextTick(() => renderer.fit())
}

// ============================================================================
// TERMINAL STYLE
// ============================================================================

const terminalStyle = computed((): Record<string, string> => {
  if (isMobile.value) {
    const bottomOffset = keyboardHeight.value > 0 ? `${keyboardHeight.value}px` : '0'
    const maxH = keyboardHeight.value > 0 ? `calc(100vh - ${keyboardHeight.value}px)` : '90vh'
    return {
      position: 'fixed',
      left: '0',
      right: '0',
      bottom: bottomOffset,
      width: '100%',
      height: `${sheetHeight.value}vh`,
      maxHeight: maxH
    }
  }

  if (!hasCustomPosition.value) {
    return {
      width: `${size.value.w}px`,
      height: `${size.value.h}px`,
      bottom: '16px',
      right: '16px'
    }
  }
  return {
    width: `${size.value.w}px`,
    height: `${size.value.h}px`,
    top: `${position.value.y}px`,
    left: `${position.value.x}px`,
    bottom: 'auto',
    right: 'auto'
  }
})

// ============================================================================
// WEBSOCKET CONNECTION
// ============================================================================

async function connect() {
  if (connecting.value || connected.value) return
  connecting.value = true

  const connectionTimeout = window.setTimeout(() => {
    if (connecting.value && !connected.value) {
      connecting.value = false
      socket?.close()
      socket = null
      if (isOpen.value && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
        scheduleReconnect()
      }
    }
  }, 10000)

  try {
    socket = new WebSocket(WS_URL)
    socket.addEventListener('open', () => clearTimeout(connectionTimeout), { once: true })

    socket.onopen = () => {
      connected.value = true
      connecting.value = false
      reconnectAttempts = 0
      renderer.focus()

      // Send initial resize
      const term = renderer.terminal.value
      if (term) {
        socket?.send(JSON.stringify({ type: 'resize', cols: term.cols, rows: term.rows }))
      }
    }

    socket.onmessage = (event) => {
      const msg = JSON.parse(event.data)

      if (msg.type === 'connected') {
        sessionId.value = msg.sessionId

        if (msg.hasHistory && isOpen.value) {
          setTimeout(() => requestReplay(), 50)
        } else if (!msg.isNew) {
          renderer.writeln('\x1b[36m[Session restored]\x1b[0m')
        }
      } else if (msg.type === 'replay') {
        renderer.handleReplay(msg.data || '')
      } else if (msg.type === 'output') {
        // Token detection
        if (waitingForToken.value) {
          tokenBuffer += msg.data
          if (tokenTimeout) clearTimeout(tokenTimeout)
          tokenTimeout = window.setTimeout(() => {
            if (tokenBuffer.includes('Token copiado')) {
              const clean = tokenBuffer.replace(/\x1b\[[0-9;]*m/g, '').replace(/[\r\n\s]/g, '')
              const match = clean.match(/eyJ[A-Za-z0-9_\-+/=]+/)
              if (match) {
                try {
                  const decoded = atob(match[0])
                  JSON.parse(decoded)
                  waitingForToken.value = false
                  tokenBuffer = ''
                  stopTokenPolling()
                  connectWithToken(match[0]).then(success => {
                    if (success) canvasStore.showNotification('WebMCP connected!', 'success')
                  }).catch(console.error)
                } catch { /* Token incomplete */ }
              }
            }
          }, 300)
        }

        // Write output using composable
        renderer.write(msg.data)
      } else if (msg.type === 'exit') {
        renderer.write(msg.data)
        sessionId.value = null
      } else if (msg.type === 'error') {
        renderer.writeln(`\x1b[31mError: ${msg.message}\x1b[0m`)
      }
    }

    socket.onclose = () => {
      connected.value = false
      connecting.value = false
      socket = null

      if (isOpen.value && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
        renderer.writeln('\x1b[33m[Disconnected - reconnecting...]\x1b[0m')
        scheduleReconnect()
      }
    }

    socket.onerror = () => {
      connecting.value = false
    }
  } catch {
    connecting.value = false
    if (isOpen.value && reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
      scheduleReconnect()
    }
  }
}

function scheduleReconnect() {
  if (reconnectTimeout) clearTimeout(reconnectTimeout)
  reconnectAttempts++
  const delay = RECONNECT_DELAY_MS * Math.min(reconnectAttempts, 5)


  reconnectTimeout = window.setTimeout(() => {
    if (isOpen.value && !connected.value && !connecting.value) {
      connect()
    }
  }, delay)
}

function cancelReconnect() {
  if (reconnectTimeout) {
    clearTimeout(reconnectTimeout)
    reconnectTimeout = null
  }
  reconnectAttempts = 0
}

// ============================================================================
// ACTIONS
// ============================================================================

function close() {
  isOpen.value = false
}

function runClaude() {
  if (socket?.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify({ type: 'input', data: 'claude\r' }))
  }
}

function runClaudeContinue() {
  if (socket?.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify({ type: 'input', data: 'claude --continue\r' }))
  }
}

function runClaudeResume() {
  if (socket?.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify({ type: 'input', data: 'claude --resume\r' }))
  }
}

function sendClear() {
  if (socket?.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify({ type: 'input', data: 'clear\r' }))
  }
}

function requestToken() {
  if (socket?.readyState === WebSocket.OPEN) {
    tokenBuffer = ''
    waitingForToken.value = true
    const text = 'genera token usando tu mcp'
    const chars = (text + '\r').split('')
    let i = 0
    const typeChar = () => {
      if (i < chars.length && socket?.readyState === WebSocket.OPEN) {
        socket.send(JSON.stringify({ type: 'input', data: chars[i] }))
        i++
        setTimeout(typeChar, 15)
      }
    }
    typeChar()
  }
}

function sendKey(key: string) {
  if (!socket || socket.readyState !== WebSocket.OPEN) return
  const keyMap: Record<string, string> = {
    'up': '\x1b[A', 'down': '\x1b[B', 'left': '\x1b[D', 'right': '\x1b[C',
    'alt-m': '\x1bm', 'ctrl-c': '\x03', 'tab': '\t', 'esc': '\x1b'
  }
  const data = keyMap[key]
  if (data) socket.send(JSON.stringify({ type: 'input', data }))
}

function scrollTerminal(direction: 'up' | 'down' | 'end') {
  if (direction === 'up') renderer.scrollLines(-10)
  else if (direction === 'down') renderer.scrollLines(10)
  else renderer.scrollToBottom()
}

function requestReplay(tailOnly = false) {
  if (socket?.readyState === WebSocket.OPEN) {
    socket.send(JSON.stringify({ type: 'request-replay', tailOnly, chunks: 500 }))
  }
}

function refreshTerminal(fullReplay = false) {
  if (!connected.value && !connecting.value) {
    reconnectAttempts = 0
    connect()
    return
  }
  requestAnimationFrame(() => {
    renderer.fit()
    requestReplay(!fullReplay)
  })
}

// ============================================================================
// WATCHERS
// ============================================================================

watch(isOpen, (open) => {
  if (open) {
    nextTick(() => {
      renderer.init()

      // Wait for CSS transition then setup
      setTimeout(() => {
        renderer.onBecameVisible()

        if (!connected.value && !connecting.value) {
          connect()
        } else if (connected.value) {
          requestReplay()
        }
      }, 150)
    })
  } else {
    renderer.blur()
    waitingForToken.value = false
    tokenBuffer = ''
    if (tokenTimeout) clearTimeout(tokenTimeout)
  }
})

watch([sheetHeight, keyboardHeight], () => {
  if (isMobile.value && isOpen.value) {
    nextTick(() => renderer.fit())
  }
})

// ============================================================================
// LIFECYCLE
// ============================================================================

onMounted(() => {
  document.addEventListener('mousemove', trackMouse)
  document.addEventListener('keydown', handleKeydown)
  checkMobile()
  window.addEventListener('resize', checkMobile)
  setupKeyboardDetection()
})

onBeforeUnmount(() => {
  cancelReconnect()
  socket?.close()
  renderer.dispose()
  document.removeEventListener('mousemove', onDrag)
  document.removeEventListener('mouseup', stopDrag)
  document.removeEventListener('touchmove', onDrag)
  document.removeEventListener('touchend', stopDrag)
  document.removeEventListener('mousemove', onResize)
  document.removeEventListener('mouseup', stopResize)
  document.removeEventListener('mousemove', trackMouse)
  document.removeEventListener('keydown', handleKeydown)
  window.removeEventListener('resize', checkMobile)
  if (window.visualViewport) {
    window.visualViewport.removeEventListener('resize', handleViewportResize)
  }
})

// ============================================================================
// EXPOSE
// ============================================================================

defineExpose({
  open: (x?: number, y?: number) => {
    if (x !== undefined && y !== undefined) {
      position.value = { x, y }
      hasCustomPosition.value = true
    }
    isOpen.value = true
  },
  close: () => { isOpen.value = false },
  toggle: toggleTerminal,
  move: (x: number, y: number) => {
    position.value = { x, y }
    hasCustomPosition.value = true
  },
  resize: (w: number, h: number) => {
    size.value = { w: Math.max(400, w), h: Math.max(250, h) }
    nextTick(() => renderer.fit())
  },
  getState: () => ({ isOpen: isOpen.value, position: position.value, size: size.value }),
  sendInput: (text: string) => {
    if (socket?.readyState === WebSocket.OPEN) {
      socket.send(JSON.stringify({ type: 'input', data: text + '\r' }))
      return true
    }
    return false
  }
})
</script>

<template>
  <Teleport to="body">
    <Transition name="win-slide">
      <div
        v-show="isOpen"
        ref="terminalRef"
        class="aero-win"
        :class="{
          dragging: isDragging,
          resizing: isResizing,
          mobile: isMobile,
          'sheet-dragging': isDraggingSheet
        }"
        :style="terminalStyle"
      >
        <div class="glass">
          <!-- Mobile drag handle -->
          <div
            v-if="isMobile"
            class="sheet-handle"
            @touchstart.passive="startSheetDrag"
            @touchmove.prevent="onSheetDrag"
            @touchend="stopSheetDrag"
          >
            <div class="handle-bar"></div>
          </div>
          <!-- Titlebar -->
          <div
            class="titlebar"
            @mousedown="startDrag"
            @touchstart.passive="startDrag"
            @touchmove="onSheetDrag"
            @touchend="stopSheetDrag"
          >
            <div class="left">
              <div class="nucleo-logo">
                <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
                  <circle cx="12" cy="12" r="4" fill="url(#nucleoGradient)"/>
                  <ellipse cx="12" cy="12" rx="9" ry="4" stroke="url(#orbitGradient)" stroke-width="1.2" fill="none" transform="rotate(-30 12 12)"/>
                  <ellipse cx="12" cy="12" rx="9" ry="4" stroke="url(#orbitGradient)" stroke-width="1.2" fill="none" transform="rotate(30 12 12)"/>
                  <ellipse cx="12" cy="12" rx="9" ry="4" stroke="url(#orbitGradient)" stroke-width="1.2" fill="none" transform="rotate(90 12 12)"/>
                  <circle cx="12" cy="3" r="1.5" fill="#a78bfa">
                    <animateTransform attributeName="transform" type="rotate" from="0 12 12" to="360 12 12" dur="3s" repeatCount="indefinite"/>
                  </circle>
                  <circle cx="20" cy="14" r="1.5" fill="#818cf8">
                    <animateTransform attributeName="transform" type="rotate" from="120 12 12" to="480 12 12" dur="4s" repeatCount="indefinite"/>
                  </circle>
                  <circle cx="4" cy="14" r="1.5" fill="#c4b5fd">
                    <animateTransform attributeName="transform" type="rotate" from="240 12 12" to="600 12 12" dur="3.5s" repeatCount="indefinite"/>
                  </circle>
                  <defs>
                    <radialGradient id="nucleoGradient" cx="50%" cy="30%" r="60%">
                      <stop offset="0%" stop-color="#a78bfa"/>
                      <stop offset="100%" stop-color="#6366f1"/>
                    </radialGradient>
                    <linearGradient id="orbitGradient" x1="0%" y1="0%" x2="100%" y2="100%">
                      <stop offset="0%" stop-color="#818cf8" stop-opacity="0.8"/>
                      <stop offset="50%" stop-color="#a78bfa" stop-opacity="0.4"/>
                      <stop offset="100%" stop-color="#818cf8" stop-opacity="0.8"/>
                    </linearGradient>
                  </defs>
                </svg>
              </div>
              <span class="nucleo-name">Nucleo</span>
              <i class="dot" :class="{ on: connected, wait: connecting }"></i>
              <a v-if="!connected && !connecting" class="link" @click.stop="connect">connect</a>
            </div>
            <div class="window-controls">
              <button @click="requestToken" :class="{ waiting: waitingForToken }" title="Connect MCP"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg></button>
              <button @click="runClaude" title="Claude"><svg width="8" height="8" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/></svg></button>
              <button @click="refreshTerminal" title="Refresh Screen"><svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg></button>
              <button class="x" @click="close" title="Close"><svg width="8" height="8" viewBox="0 0 10 10"><line x1="0" y1="0" x2="10" y2="10" stroke="currentColor" stroke-width="1.5"/><line x1="10" y1="0" x2="0" y2="10" stroke="currentColor" stroke-width="1.5"/></svg></button>
            </div>
          </div>
          <!-- Content -->
          <div class="content">
            <div ref="terminalContainer" class="term"></div>
            <div v-if="!connected && !connecting" class="disconnect-overlay" @click="connect">
              <div class="disconnect-msg">
                <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
                  <path d="M18.36 6.64a9 9 0 1 1-12.73 0"/>
                  <line x1="12" y1="2" x2="12" y2="12"/>
                </svg>
                <span>Desconectado</span>
                <small>Toca para reconectar</small>
              </div>
            </div>
            <div v-else-if="connecting" class="disconnect-overlay connecting">
              <div class="disconnect-msg">
                <div class="spinner"></div>
                <span>Conectando...</span>
              </div>
            </div>
          </div>
          <!-- Mobile virtual keys -->
          <div v-if="isMobile" class="virtual-keys">
            <button @click="sendKey('esc')" class="vk">Esc</button>
            <button @click="sendKey('tab')" class="vk">Tab</button>
            <button @click="sendKey('ctrl-c')" class="vk ctrl">^C</button>
            <button @click="sendKey('alt-m')" class="vk alt">Alt+M</button>
            <button @click="sendClear" class="vk clear">Clear</button>
            <button @click="requestToken" class="vk token" :class="{ waiting: waitingForToken }">MCP</button>
            <button @click="runClaude" class="vk claude">Claude</button>
            <button @click="runClaudeContinue" class="vk claude-cont">Cont</button>
            <button @click="runClaudeResume" class="vk claude-resume">Resume</button>
            <button @click="refreshTerminal" class="vk refresh">↻</button>
            <div class="vk-scroll">
              <button @click="scrollTerminal('up')" class="vk scroll">⬆</button>
              <button @click="scrollTerminal('end')" class="vk scroll end">⤓</button>
              <button @click="scrollTerminal('down')" class="vk scroll">⬇</button>
            </div>
            <div class="vk-arrows">
              <button @click="sendKey('up')" class="vk arrow">▲</button>
              <div class="vk-row">
                <button @click="sendKey('left')" class="vk arrow">◀</button>
                <button @click="sendKey('down')" class="vk arrow">▼</button>
                <button @click="sendKey('right')" class="vk arrow">▶</button>
              </div>
            </div>
          </div>
          <div class="resize-handle" @mousedown="startResize"></div>
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
.aero-win {
  position: fixed;
  min-width: 400px;
  min-height: 250px;
  z-index: 9999;
}

.glass {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: rgba(200,215,235,0.35);
  backdrop-filter: blur(24px) saturate(1.6);
  -webkit-backdrop-filter: blur(24px) saturate(1.6);
  border-radius: 5px;
  border: 1px solid rgba(255,255,255,0.6);
  box-shadow: 0 0 0 1px rgba(80,120,180,0.25), 0 6px 24px rgba(0,0,0,0.25), inset 0 1px 0 rgba(255,255,255,0.6);
  overflow: hidden;
}

.titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 22px;
  padding: 0 2px 0 6px;
  background: rgba(255,255,255,0.25);
  border-bottom: 1px solid rgba(255,255,255,0.3);
  cursor: grab;
  user-select: none;
}
.aero-win.dragging .titlebar { cursor: grabbing; }

.left {
  display: flex;
  align-items: center;
  gap: 6px;
  color: #222;
  font: 500 10px/1 system-ui, sans-serif;
}

.nucleo-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  filter: drop-shadow(0 1px 2px rgba(99, 102, 241, 0.3));
}

.nucleo-name {
  font-weight: 600;
  font-size: 11px;
  background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
  letter-spacing: 0.5px;
}

.dot { width: 5px; height: 5px; border-radius: 50%; background: #999; }
.dot.on { background: #0a0; box-shadow: 0 0 4px #0a0; }
.dot.wait { background: #a80; animation: pulse .8s infinite; }

.link { margin-left: 2px; color: #369; font-size: 9px; text-decoration: underline; cursor: pointer; }
.link:hover { color: #47a; }

.window-controls { display: flex; gap: 1px; }
.window-controls button {
  width: 20px; height: 16px;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.3);
  border: 1px solid rgba(0,0,0,0.1);
  border-radius: 2px;
  color: #333;
  cursor: pointer;
}
.window-controls button:hover { background: rgba(255,255,255,0.5); }
.window-controls button.x:hover { background: linear-gradient(180deg, #e66 0%, #c33 100%); border-color: #a22; color: #fff; }
.window-controls button.waiting { background: rgba(16, 185, 129, 0.3); border-color: #10b981; animation: pulse 0.8s infinite; }

.content {
  flex: 1;
  margin: 2px;
  border-radius: 2px;
  overflow: hidden;
  background: rgba(0,0,0,0.92);
  position: relative;
}

.disconnect-overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
  cursor: pointer;
}
.disconnect-overlay.connecting { cursor: wait; }

.disconnect-msg {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  color: #fff;
  text-align: center;
}
.disconnect-msg svg { color: #ef4444; opacity: 0.8; }
.disconnect-msg span { font-size: 14px; font-weight: 500; }
.disconnect-msg small { font-size: 11px; color: #888; }

.spinner {
  width: 24px; height: 24px;
  border: 2px solid rgba(255, 255, 255, 0.2);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 0.8s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

.resize-handle {
  position: absolute;
  right: 0; bottom: 0;
  width: 16px; height: 16px;
  cursor: nwse-resize;
  background: linear-gradient(135deg, transparent 50%, rgba(255,255,255,0.3) 50%, rgba(255,255,255,0.1) 100%);
  border-radius: 0 0 5px 0;
}
.resize-handle:hover { background: linear-gradient(135deg, transparent 50%, rgba(255,255,255,0.5) 50%, rgba(255,255,255,0.2) 100%); }

.aero-win.resizing { user-select: none; }
.aero-win.resizing .term { pointer-events: none; }

.term { width: 100%; height: 100%; }
.term :deep(.xterm) { height: 100%; padding: 2px; }
.term :deep(.xterm-viewport) {
  overflow-y: auto !important;
  -webkit-overflow-scrolling: touch;
  touch-action: pan-y;
}
.term :deep(.xterm-screen) { touch-action: pan-y; }
.term :deep(.xterm-viewport::-webkit-scrollbar) { width: 8px; background: rgba(0,0,0,0.2); }
.term :deep(.xterm-viewport::-webkit-scrollbar-thumb) { background: rgba(255,255,255,0.15); border-radius: 4px; }
.term :deep(.xterm-viewport::-webkit-scrollbar-thumb:hover) { background: rgba(255,255,255,0.25); }

.win-slide-enter-active, .win-slide-leave-active { transition: all .15s ease; }
.win-slide-enter-from, .win-slide-leave-to { opacity: 0; transform: translateY(16px) scale(0.98); }

@keyframes pulse { 0%,100% { opacity:1; } 50% { opacity:.4; } }

/* Mobile */
.aero-win.mobile { min-width: unset; min-height: unset; max-width: 100%; transition: height 0.25s cubic-bezier(0.4, 0, 0.2, 1); }
.aero-win.mobile.sheet-dragging { transition: none; }
.aero-win.mobile .glass { border-radius: 16px 16px 0 0; padding-bottom: env(safe-area-inset-bottom, 0); }

.sheet-handle {
  display: flex;
  justify-content: center;
  align-items: center;
  height: 24px;
  cursor: grab;
  touch-action: none;
  background: rgba(255,255,255,0.15);
}
.sheet-handle:active { cursor: grabbing; }
.handle-bar { width: 36px; height: 4px; background: rgba(0,0,0,0.3); border-radius: 2px; }

.aero-win.mobile .titlebar { cursor: grab; touch-action: none; }
.aero-win.mobile .resize-handle { display: none; }
.aero-win.mobile .content { flex: 1; min-height: 100px; touch-action: pan-y; overflow: hidden; }

.aero-win.mobile.win-slide-enter-from,
.aero-win.mobile.win-slide-leave-to { transform: translateY(100%); opacity: 1; }

/* Virtual keys */
.virtual-keys {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 10px;
  background: rgba(30, 30, 30, 0.95);
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  flex-shrink: 0;
  flex-wrap: wrap;
}

.vk {
  min-width: 48px; height: 40px; padding: 0 10px;
  background: linear-gradient(180deg, #444 0%, #333 100%);
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 6px;
  color: #fff;
  font-size: 13px; font-weight: 500; font-family: system-ui, sans-serif;
  cursor: pointer;
  touch-action: manipulation;
  transition: all 0.1s;
}
.vk:active { background: linear-gradient(180deg, #555 0%, #444 100%); transform: scale(0.95); }
.vk.ctrl { background: linear-gradient(180deg, #c53030 0%, #9b2c2c 100%); border-color: rgba(255, 100, 100, 0.3); }
.vk.alt { background: linear-gradient(180deg, #2563eb 0%, #1d4ed8 100%); border-color: rgba(100, 150, 255, 0.3); }
.vk.refresh { background: linear-gradient(180deg, #f59e0b 0%, #d97706 100%); border-color: rgba(245, 158, 11, 0.3); font-size: 18px; }
.vk.clear { background: linear-gradient(180deg, #6b7280 0%, #4b5563 100%); border-color: rgba(107, 114, 128, 0.3); }
.vk.token { background: linear-gradient(180deg, #ec4899 0%, #db2777 100%); border-color: rgba(236, 72, 153, 0.3); }
.vk.token.waiting { background: linear-gradient(180deg, #10b981 0%, #059669 100%); border-color: rgba(16, 185, 129, 0.3); animation: pulse 0.8s infinite; }
.vk.claude { background: linear-gradient(180deg, #8b5cf6 0%, #7c3aed 100%); border-color: rgba(139, 92, 246, 0.3); }
.vk.claude-cont { background: linear-gradient(180deg, #06b6d4 0%, #0891b2 100%); border-color: rgba(6, 182, 212, 0.3); }
.vk.claude-resume { background: linear-gradient(180deg, #10b981 0%, #059669 100%); border-color: rgba(16, 185, 129, 0.3); }

.vk-scroll { display: flex; flex-direction: column; align-items: center; gap: 2px; margin-left: auto; }
.vk.scroll { min-width: 44px; width: 44px; height: 36px; padding: 0; font-size: 16px; background: linear-gradient(180deg, #065f46 0%, #047857 100%); border-color: rgba(16, 185, 129, 0.3); }
.vk.scroll.end { background: linear-gradient(180deg, #7c3aed 0%, #6d28d9 100%); border-color: rgba(139, 92, 246, 0.3); font-size: 18px; }

.vk-arrows { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.vk-row { display: flex; gap: 2px; }
.vk.arrow { min-width: 38px; width: 38px; height: 32px; padding: 0; font-size: 12px; }
</style>
