<template>
  <main class="dashboard" :data-theme="isDark ? 'dark' : 'light'">
    <header class="header">
      <div class="logo-area">
        <svg class="logo-icon" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
          <rect x="12" y="18" width="40" height="28" rx="6" stroke="var(--accent)" stroke-width="3"/>
          <circle cx="24" cy="32" r="5" fill="var(--icon-red, #ef4444)"/>
          <circle cx="40" cy="32" r="5" fill="var(--icon-red, #ef4444)"/>
          <rect x="26" y="44" width="12" height="4" rx="2" fill="var(--icon-gray)"/>
          <line x1="8" y1="52" x2="12" y2="52" stroke="var(--accent)" stroke-width="3" stroke-linecap="round"/>
          <line x1="52" y1="52" x2="56" y2="52" stroke="var(--accent)" stroke-width="3" stroke-linecap="round"/>
        </svg>
        <div class="logo-text">
          <h1>QUIBOT</h1>
          <span class="subtitle">{{ $t('header.subtitle') }}</span>
        </div>
      </div>

     <div class="status-bar">
        <div v-if="connected" class="status-indicator online">
          <span class="pulse"></span>
          {{ $t('header.online') }}
        </div>
        <div v-else class="status-indicator offline">
          <span class="pulse pulse-red"></span>
          {{ $t('header.offline') }}
        </div>
        <span class="robot-mode">{{ robotModeLabel }}</span>
        <button class="settings-btn" :aria-label="$t('settings.title')" @click="showSettings = !showSettings">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12.22 2h-.44a2 2 0 00-2 2v.18a2 2 0 01-1 1.73l-.43.25a2 2 0 01-2 0l-.15-.08a2 2 0 00-2.73.73l-.22.38a2 2 0 00.73 2.73l.15.1a2 2 0 011 1.72v.51a2 2 0 01-1 1.74l-.15.09a2 2 0 00-.73 2.73l.22.38a2 2 0 002.73.73l.15-.08a2 2 0 012 0l.43.25a2 2 0 011 1.73V20a2 2 0 002 2h.44a2 2 0 002-2v-.18a2 2 0 011-1.73l.43-.25a2 2 0 012 0l.15.08a2 2 0 002.73-.73l.22-.39a2 2 0 00-.73-2.73l-.15-.08a2 2 0 01-1-1.74v-.5a2 2 0 011-1.74l.15-.09a2 2 0 00.73 2.73l.22.38a2 2 0 00-2.73-.73l-.15.08a2 2 0 01-2 0l.43.25a2 2 0 01-1-1.73V4a2 2 0 00-2 2z"/><circle cx="12" cy="12" r="3"/></svg>
        </button>
      </div>
    </header>

    <!-- Settings Modal -->
    <SettingsModal :show="showSettings" @update:show="(v) => showSettings = v" @toast="handleToast($event)" />

    <div class="panels-grid">
      <div class="row-top">
        <!-- Block Queue Panel -->
        <section class="panel block-queue-panel">
          <div class="panel-header">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--icon-purple)" stroke-width="2"><rect x="3" y="3" width="7" height="18" rx="1"/><path d="M14 6h5a1 1 0 011 1v10a1 1 0 01-1 1h-5"/><path d="M12 3v18"/></svg>
            <h2>{{ $t('panels.blockQueue') }}</h2>
          </div>

          <div v-if="blocks.length === 0" class="empty-state">
            <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="var(--empty-icon)" stroke-width="1.5"><path d="M20 7H4a2 2 0 00-2 2v6a2 2 0 002 2h16a2 2 0 002-2V9a2 2 0 00-2-2z"/><path d="M16 7V5a2 2 0 00-2-2h-4a2 2 0 00-2 2v2"/></svg>
            <p>{{ $t('blocks.empty') }}</p>
          </div>

          <div v-else class="block-list">
            <div v-for="(block, index) in blocks" :key="index" class="block-item">
              <div class="block-number">{{ index + 1 }}</div>
              <div class="block-color-indicator" :style="{ background: block.colorHex }">
                <span v-if="block.processing" class="processing-ring"></span>
              </div>
              <div class="block-info">
                <span class="block-name">{{ block.name }}</span>
                <span v-if="block.processing" class="status-badge processing">{{ $t('blocks.processing') }}</span>
                <span v-else class="status-badge queued">{{ $t('blocks.queued') }}</span>
              </div>
              <div v-if="block.action" class="block-action">{{ block.actionText }}</div>
            </div>
          </div>

          <button v-if="blocks.length > 0" class="btn-sm btn-danger" :disabled="busy" @click="clearBlocks">{{ $t('blocks.clear') }}</button>
        </section>

        <!-- Motion Controls Panel -->
        <section class="panel motion-panel">
          <div class="panel-header">
            <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M12 2v4m0 12v4m10-10h-4M6 12H2"/></svg>
            <h2>{{ $t('panels.motionControls') }}</h2>
          </div>

          <div class="d-pad">
            <button class="btn-direction up" :disabled="busy" @click="doAction('forward')">
              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 19V5m-7 7l7-7 7 7"/></svg>
              <span>{{ $t('motion.forward') }}</span>
            </button>
            <button class="btn-direction left" :disabled="busy" @click="doAction('left')">
              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M19 12H5m7 7l-7-7 7-7"/></svg>
              <span>{{ $t('motion.left') }}</span>
            </button>
            <button class="btn-direction center" :disabled="busy" @click="doAction('stop')">
              <svg width="28" height="28" viewBox="0 0 24 24" fill="currentColor"><rect x="4" y="4" width="16" height="16" rx="3"/></svg>
            </button>
            <button class="btn-direction right" :disabled="busy" @click="doAction('right')">
              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M5 12h14m-7-7l7 7-7 7"/></svg>
              <span>{{ $t('motion.right') }}</span>
            </button>
            <button class="btn-direction down" :disabled="busy" @click="doAction('backward')">
              <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M12 5v14m7-7l-7 7-7-7"/></svg>
              <span>{{ $t('motion.backward') }}</span>
            </button>
          </div>

          <p v-if="currentAction" class="action-status">{{ currentAction }}</p>
        </section>
      </div>

      <!-- Eye Controls Panel -->
      <section class="panel eye-panel">
        <div class="panel-header">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--icon-red)" stroke-width="2"><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8z"/><circle cx="12" cy="12" r="3"/></svg>
          <h2>{{ $t('panels.eyeControls') }}</h2>
        </div>

        <!-- Eye shape grid -->
        <div class="eye-shape-grid">
          <button v-for="shape in eyeShapes" :key="shape.id"
            class="btn-shape" :class="{ active: selectedShape === shape.id }"
            @click="selectedShape = shape.id">
            <div class="eye-preview">
              <svg viewBox="0 0 80 40" class="eye-svg">
                <template v-if="shape.id === 'open'">
                  <rect x="2" y="6" width="30" height="28" rx="14" :fill="eyeColorHex" stroke="#fff" stroke-width="1.5"/>
                  <rect x="48" y="6" width="30" height="28" rx="14" :fill="eyeColorHex" stroke="#fff" stroke-width="1.5"/>
                  <circle cx="17" cy="20" r="4" fill="#111827"/>
                  <circle cx="63" cy="20" r="4" fill="#111827"/>
                </template>
                <template v-if="shape.id === 'fw'">
                  <rect x="2" y="6" width="30" height="28" rx="14" :fill="eyeColorHex" stroke="#fff" stroke-width="1.5"/>
                  <rect x="48" y="6" width="30" height="28" rx="14" :fill="eyeColorHex" stroke="#fff" stroke-width="1.5"/>
                  <rect x="10" y="12" width="6" height="8" rx="2" fill="#111827"/>
                  <rect x="56" y="12" width="6" height="8" rx="2" fill="#111827"/>
                </template>
                <template v-if="shape.id === 'down'">
                  <rect x="2" y="6" width="30" height="28" rx="14" :fill="eyeColorHex" stroke="#fff" stroke-width="1.5"/>
                  <rect x="48" y="6" width="30" height="28" rx="14" :fill="eyeColorHex" stroke="#fff" stroke-width="1.5"/>
                  <rect x="12" y="20" width="6" height="5" rx="2" fill="#111827"/>
                  <rect x="58" y="20" width="6" height="5" rx="2" fill="#111827"/>
                </template>
                <template v-if="shape.id === 'gesture'">
                  <rect x="2" y="2" width="30" height="36" rx="14" :fill="eyeColorHex" :style="{ stroke: 'var(--icon-cyan)' }" stroke-width="2" opacity="0.8"/>
                  <rect x="48" y="2" width="30" height="36" rx="14" :fill="eyeColorHex" :style="{ stroke: 'var(--icon-cyan)' }" stroke-width="2" opacity="0.8"/>
                </template>
              </svg>
            </div>
            <span>{{ $t(`eyeShapes.${shape.labelKey}`) }}</span>
          </button>
        </div>

        <!-- Apply shape button -->
        <button class="btn-md" :disabled="busy || !selectedShape" @click="applyEyeShape">{{ $t('eyes.applyShape') }}</button>

        <!-- Eye color selector -->
        <h3 class="section-label">{{ $t('eyes.shapeLabel') }}</h3>
        <div class="color-grid">
          <button v-for="c in eyeColors" :key="c.id"
            class="color-btn" :class="{ active: selectedColor === c.id }"
            :style="{ background: c.hex }"
            :title="c.name"
            @click="selectedColor = c.id">
            <span v-if="selectedColor === c.id" class="check-icon">&#10003;</span>
          </button>
        </div>

        <button class="btn-md" :disabled="busy" @click="applyEyeColor">{{ $t('eyes.applyColor') }}</button>

        <!-- Eye special actions -->
        <h3 class="section-label">{{ $t('eyes.actionsLabel') }}</h3>
        <div class="action-row">
          <button class="btn-sm" :disabled="busy" @click="eyeOn">{{ $t('eyes.eyeOn') }}</button>
          <button class="btn-sm btn-danger" :disabled="busy" @click="eyeOff">{{ $t('eyes.eyeOff') }}</button>
        </div>
      </section>

      <!-- Gesture Sensor Panel -->
      <section class="panel gesture-panel">
        <div class="panel-header">
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="var(--status-green)" stroke-width="2"><path d="M18 11V6a2 2 0 00-4 0v1M14 10V4a2 2 0 00-4 0v2m4 2h.01M20 11v5a2 2 0 01-4 0v-3"/><path d="M6.5 11.5V17a5.5 5.5 0 0011 0v-4"/></svg>
          <h2>{{ $t('panels.gestureSensor') }}</h2>
        </div>

        <!-- Mode toggle -->
        <div class="mode-card">
          <span class="section-label">{{ $t('gestures.modeLabel') }}</span>
          <div class="mode-switch">
            <button :class="['btn-mode', { active: gestureMode === 'block' }]" @click="setGestureMode('block')">
               <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="18" rx="1"/><path d="M14 6h5a1 1 0 011 1v10a1 1 0 01-1 1h-5"/></svg>
               {{ $t('gestures.blockMode') }}
             </button>
            <button :class="['btn-mode', { active: gestureMode === 'gesture' }]" @click="setGestureMode('gesture')">
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M18 11V6a2 2 0 00-4 0v1M14 10V4a2 2 0 00-4 0v2m4 2h.01"/></svg>
              {{ $t('gestures.gestureMode') }}
            </button>
          </div>
        </div>

        <!-- Gesture detection history -->
        <h3 class="section-label">{{ $t('gestures.detectedLabel') }}</h3>

        <div v-if="gestureLog.length === 0" class="empty-state">
          <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="var(--empty-icon)" stroke-width="1.5"><path d="M18 11V6a2 2 0 00-4 0v1M14 10V4a2 2 0 00-4 0v2m4 2h.01M20 11v5a2 2 0 01-4 0v-3"/><path d="M6.5 11.5V17a5.5 5.5 0 0011 0v-4"/></svg>
          <p>{{ $t('gestures.empty') }}</p>
        </div>

        <div v-else class="gesture-list">
          <div v-for="(gesture, index) in gestureLog" :key="index" class="gesture-item" :class="'gesture-' + gesture.id.toLowerCase().replace('_', '')">
            <div class="gesture-icon">{{ gesture.emoji }}</div>
            <div class="gesture-info">
              <span class="gesture-name">{{ gesture.name }}</span>
              <span v-if="gesture.action" class="gesture-action-text">{{ gesture.actionText }}</span>
            </div>
            <span class="gesture-time">{{ gesture.time }}</span>
          </div>
        </div>

        <!-- Gesture quick reference -->
        <details class="gesture-ref">
          <summary class="ref-toggle">{{ $t('gestures.reference') }}</summary>
          <div class="ref-grid">
            <div v-for="g in availableGestures" :key="g.id" class="ref-item">
              <span class="ref-emoji">{{ g.emoji }}</span>
              <span class="ref-name">{{ $t(`gestureRef.${g.nameKey}`) }}</span>
            </div>
          </div>
        </details>

        <button class="btn-sm" :disabled="busy" @click="clearGestureLog">{{ $t('gestures.clearLog') }}</button>
      </section>
    </div>

    <!-- Toast message -->
    <transition name="toast">
      <div v-if="toastMsg" class="toast" :class="toastType">
        {{ toastMsg }}
      </div>
    </transition>
  </main>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'

const { t } = useI18n()

// ── Settings ──
const showSettings = ref(false)

// ── State ──
const busy = ref(false)
const currentAction = ref('')
const message = ref('')
const error = ref('')
const connected = ref(true)
const toastMsg = ref('')
const toastType = ref<'success' | 'error' | ''>('')

const isDark = ref(() => {
  const saved = localStorage.getItem('quibot-theme')
  if (saved) return saved === 'dark'
  return window.matchMedia('(prefers-color-scheme: dark)').matches
})

let toastTimer: ReturnType<typeof setTimeout> | null = null
let themeMediaQuery: MediaQueryList | null = null

function toggleTheme() {
  isDark.value = !isDark.value
  localStorage.setItem('quibot-theme', isDark.value ? 'dark' : 'light')
  applyTheme()
}

function applyTheme() {
  document.documentElement.setAttribute('data-theme', isDark.value ? 'dark' : 'light')
}

// ── Block Queue ──
interface BlockInfo {
  name: string
  colorHex: string
  processing: boolean
  actionText?: string
}

const blocks = ref<BlockInfo[]>([])

const blockColorMap = [
  { id: 'RD', key: 'red', hex: '#ef4444' },
  { id: 'GN', key: 'green', hex: '#22c55e' },
  { id: 'BU', key: 'blue', hex: '#3b82f6' },
  { id: 'YE', key: 'yellow', hex: '#eab308' },
  { id: 'OG', key: 'orange', hex: '#f97316' },
  { id: 'VT', key: 'violet', hex: '#a855f7' },
]

interface BlockColorEntry {
  name: string
  hex: string
}

function getBlockColor(key: string): BlockColorEntry {
  const c = blockColorMap.find(b => b.key === key)
  return {
    name: c ? t(`colors.${c.key}`) : key,
    hex: c?.hex || '#888',
  }
}

function updateBlocksFromResponse() {
  try {
    const data = JSON.parse(localStorage.getItem('quibot_blocks') || '{}') as Record<string, boolean>
    blocks.value = []
    for (const [colorId, present] of Object.entries(data)) {
      const c = blockColorMap.find(b => b.id === colorId)
      if (c && present) {
        const colorInfo = getBlockColor(c.key)
        blocks.value.push({
          name: `${colorInfo.name} [${colorId}]`,
          colorHex: c.hex,
          processing: false,
          actionText: t(`blockActions.${c.key.toLowerCase()}`),
        })
      }
    }
  } catch {
    // ignore
  }

  // Demo: simulate 2 blocks if none available yet
  if (blocks.value.length === 0) {
    const rdInfo = getBlockColor('red')
    const gnInfo = getBlockColor('green')
    blocks.value = [
      { name: `${rdInfo.name} [RD]`, colorHex: '#ef4444', processing: false, actionText: t('blockActions.rd') },
      { name: `${gnInfo.name} [GN]`, colorHex: '#22c55e', processing: false, actionText: t('blockActions.gn') },
    ]
  }
}

function clearBlocks() {
  blocks.value = []
  localStorage.removeItem('quibot_blocks')
  showToast(t('toast.cleared'), 'success')
}

// ── Motion Controls ──
type Direction = 'forward' | 'backward' | 'left' | 'right' | 'stop'

async function doAction(dir: Direction) {
  currentAction.value = t('motion.sending', { dir })
  message.value = ''
  error.value = ''
  busy.value = true

  try {
    if (dir === 'stop') {
      await $fetch('/api/motor/stop', { method: 'POST' })
    } else {
      const validDir = dir === 'left' ? 'forward' : dir === 'right' ? 'backward' : dir
      await $fetch(`/api/motor/step/${validDir}`, { method: 'POST' })
    }
    message.value = t('motion.sent', { dir })
    showToast(t('motionToast.success', { dir }), 'success')
  } catch (err: unknown) {
    const e = err as Record<string, unknown>
    error.value = String(e?.message || 'Request failed')
    showToast(t('motion.failed'), 'error')
  } finally {
    busy.value = false
    currentAction.value = ''
  }
}

// ── Eye Controls ──
interface ShapeInfo {
  id: string
  labelKey: string
}

const eyeShapes: ShapeInfo[] = [
  { id: 'open', labelKey: 'open' },
  { id: 'fw', labelKey: 'fw' },
  { id: 'down', labelKey: 'down' },
  { id: 'gesture', labelKey: 'gesture' },
]

interface ColorInfo {
  id: string
  nameKey: string
  hex: string
}

const eyeColors: ColorInfo[] = [
  { id: 'white', nameKey: 'white', hex: '#f8fafc' },
  { id: 'red', nameKey: 'red', hex: '#ef4444' },
  { id: 'green', nameKey: 'green', hex: '#22c55e' },
  { id: 'blue', nameKey: 'blue', hex: '#3b82f6' },
  { id: 'yellow', nameKey: 'yellow', hex: '#eab308' },
  { id: 'orange', nameKey: 'orange', hex: '#f97316' },
  { id: 'purple', nameKey: 'purple', hex: '#a855f7' },
  { id: 'cyan', nameKey: 'cyan', hex: '#06b6d4' },
  { id: 'black', nameKey: 'black', hex: '#111827' },
]

function getEyeColorName(key: string) {
  return t(`eyeColors.${key}`)
}

const selectedShape = ref('open')
const selectedColor = ref('white')

const eyeColorHex = computed(() => {
  const c = eyeColors.find(e => e.id === selectedColor.value)
  return c ? c.hex : '#f8fafc'
})

async function applyEyeShape() {
  busy.value = true
  currentAction.value = `Setting eye shape: ${selectedShape.value}`
  try {
    await $fetch('/api/eye/shape', { method: 'POST', body: { shape: selectedShape.value } })
    showToast(t('eyes.toastSetShape', { shape: selectedShape.value }), 'success')
  } catch {
    showToast(t('eyes.toastFailedShape'), 'error')
  } finally {
    busy.value = false
    currentAction.value = ''
  }
}

async function applyEyeColor() {
  busy.value = true
  currentAction.value = `Setting eye color: ${selectedColor.value}`
  try {
    await $fetch('/api/eye/color', { method: 'POST', body: { color: selectedColor.value } })
    showToast(t('eyes.toastSetColor', { color: selectedColor.value }), 'success')
  } catch {
    showToast(t('eyes.toastFailedColor'), 'error')
  } finally {
    busy.value = false
    currentAction.value = ''
  }
}

async function eyeOn() {
  busy.value = true
  try {
    await $fetch('/api/eye/on', { method: 'POST' })
    showToast(t('eyes.toastOn'), 'success')
  } catch {
    showToast(t('eyes.toastFailedOn'), 'error')
  } finally {
    busy.value = false
  }
}

async function eyeOff() {
  busy.value = true
  try {
    await $fetch('/api/eye/off', { method: 'POST' })
    showToast(t('eyes.toastOff'), 'success')
  } catch {
    showToast(t('eyes.toastFailedOff'), 'error')
  } finally {
    busy.value = false
  }
}

// ── Gesture Sensor ──
interface GestureEntry {
  id: string
  name: string
  emoji: string
  actionText: string
  time: string
}

const availableGestures = [
  { id: 'GS_FORWARD', nameKey: 'forward', emoji: '\u27A1\uFE0F' },
  { id: 'GS_LEFT', nameKey: 'left', emoji: '\uD83D\uDC4B' },
  { id: 'GS_RIGHT', nameKey: 'right', emoji: '\uD83D\uDC4B' },
  { id: 'GS_UP', nameKey: 'up', emoji: '\u2191\uFE0F' },
  { id: 'GS_DOWN', nameKey: 'down', emoji: '\u2193\uFE0F' },
  { id: 'GS_CLOCKWISE', nameKey: 'clockwise', emoji: '\uD83D\uDD01' },
  { id: 'GS_ANTICLOCKWISE', nameKey: 'anticlockwise', emoji: '\uD83D\uDD02' },
  { id: 'GS_WAVE', nameKey: 'wave', emoji: '\uD83D\uDC4B\uD83C\uDFF8' },
]

const gestureLog = ref<GestureEntry[]>([])

interface GestureActionInfo {
  nameKey: string
  actionKey: string
  emoji: string
}

const gestureActionMap: Record<string, GestureActionInfo> = {
  GS_FORWARD: { nameKey: 'forward', actionKey: 'forward', emoji: '\u27A1\uFE0F' },
  GS_RIGHT: { nameKey: 'right', actionKey: 'right', emoji: '\uD83D\uDC4B' },
  GS_LEFT: { nameKey: 'left', actionKey: 'left', emoji: '\uD83D\uDC4B' },
  GS_UP: { nameKey: 'up', actionKey: 'up', emoji: '\u2191\uFE0F' },
  GS_DOWN: { nameKey: 'down', actionKey: 'down', emoji: '\u2193\uFE0F' },
  GS_CLOCKWISE: { nameKey: 'clockwise', actionKey: 'clockwise', emoji: '\uD83D\uDD01' },
  GS_ANTICLOCKWISE: { nameKey: 'anticlockwise', actionKey: 'anticlockwise', emoji: '\uD83D\uDD02' },
  GS_WAVE: { nameKey: 'wave', actionKey: 'wave', emoji: '\uD83D\uDC4B\uD83C\uDFF8' },
}

const gestureMode = ref<'block' | 'gesture'>('block')

const robotModeLabel = computed(() => {
  return t(`modes.${gestureMode.value === 'gesture' ? 'gesture' : 'block'}`)
})

async function setGestureMode(mode: 'block' | 'gesture') {
  gestureMode.value = mode
  connected.value = true
  showToast(t('toast.switched', { mode: t(`modes.${mode}`) }), 'success')
  try {
    if (mode === 'gesture') {
      await $fetch('/api/gesture/on', { method: 'POST' })
    } else {
      await $fetch('/api/gesture/off', { method: 'POST' })
    }
  } catch {
    // ignore backend not ready
  }
}

function addGestureLog(gestureId: string) {
  const info = gestureActionMap[gestureId]
  if (!info) return
  const now = new Date()
  const timeStr = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`
  gestureLog.value.unshift({
    id: gestureId,
    name: t(`gestureNames.${info.nameKey}`),
    emoji: info.emoji,
    actionText: t(`gestureActions.${info.actionKey}`),
    time: timeStr,
  })
  // Keep at most 20
  if (gestureLog.value.length > 20) {
    gestureLog.value = gestureLog.value.slice(0, 20)
  }
}

function clearGestureLog() {
  gestureLog.value = []
}

// Simulate a gesture detection every few seconds for demo
let gestureInterval: ReturnType<typeof setInterval> | null = null
const fakeGestures = ['GS_FORWARD', 'GS_RIGHT', 'GS_LEFT', 'GS_UP', 'GS_DOWN']

function startGestureDemo() {
  // Load demo gestures from localStorage if available
  try {
    const saved = JSON.parse(localStorage.getItem('quibot_gestures') || '[]') as string[]
    for (const gId of saved) {
      addGestureLog(gId)
    }
  } catch {
    // ignore
  }

  gestureInterval = setInterval(() => {
    const fake = fakeGestures[Math.floor(Math.random() * fakeGestures.length)]
    if (gestureMode.value === 'gesture') {
      addGestureLog(fake)
    }
  }, 4000)
}

// ── Toast ──
function handleToast(msgType: [string, 'success' | 'error']) {
  showToast(msgType[0], msgType[1])
}

function showToast(msg: string, type: 'success' | 'error') {
  toastMsg.value = msg
  toastType.value = type
  if (toastTimer) clearTimeout(toastTimer)
  toastTimer = setTimeout(() => {
    toastMsg.value = ''
    toastType.value = ''
  }, 3000)
}

// ── Lifecycle ──
onMounted(() => {
  applyTheme()
  updateBlocksFromResponse()
  startGestureDemo()
  themeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
  themeMediaQuery.addEventListener('change', (e) => {
    if (!localStorage.getItem('quibot-theme')) {
      isDark.value = e.matches
      applyTheme()
    }
  })

  // Sync cookie for server routes
  const savedPI = localStorage.getItem('quibot-pi-url') || ''
  document.cookie = `quibot-pi-url=${encodeURIComponent(savedPI)};path=/`
  const savedTheme = localStorage.getItem('quibot-theme') || ''
  document.cookie = `quibot-theme=${encodeURIComponent(savedTheme)};path=/`
})

onUnmounted(() => {
  if (themeMediaQuery) {
    themeMediaQuery.removeEventListener('change', () => {})
  }
  if (gestureInterval) clearInterval(gestureInterval)
  if (toastTimer) clearTimeout(toastTimer)
})
</script>

<style scoped>
/* ── Theme Variables ── */
:global(:root[data-theme="dark"]) {
  --bg-primary: #0f172a;
  --bg-secondary: #1e293b;
  --bg-tertiary: #0f172a;
  --bg-panel: #1e293b;
  --border-color: #334155;
  --border-subtle: #475569;
  --text-primary: #f1f5f9;
  --text-secondary: #e2e8f0;
  --text-muted: #94a3b8;
  --text-dim: #64748b;
  --text-ghost: #475569;
  --accent: #f97316;
  --accent-glow: rgba(249, 115, 22, 0.3);
  --accent-hover: #fb923c;
  --gradient-start: #1e293b;
  --gradient-end: #0f172a;
  --card-bg: #1e293b;
  --item-bg: #0f172a;
  --btn-bg: #334155;
  --btn-hover: #475569;
  --status-online-bg: rgba(34, 197, 94, 0.1);
  --status-online-border: rgba(34, 197, 94, 0.2);
  --status-offline-bg: rgba(239, 68, 68, 0.1);
  --status-offline-border: rgba(239, 68, 68, 0.2);
  --badge-processing-bg: rgba(251, 191, 36, 0.1);
  --badge-queued-bg: rgba(139, 92, 246, 0.1);
  --header-shadow: 0 4px 24px rgba(0, 0, 0, 0.3);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.3);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.3);
  --active-bg: rgba(249, 115, 22, 0.1);
  --active-box: 0 0 12px rgba(249, 115, 22, 0.2);
  --status-online-color: #10b981;
  --status-online-glow: rgba(16, 185, 129, 0.2);
  --scrollbar-track: transparent;
  --scrollbar-thumb: #334155;
  --icon-purple: #8b5cf6;
  --icon-red: #ef4444;
  --icon-gray: #9ca3af;
  --accent-solid: #f97316;
  --status-green: #10b981;
  --icon-cyan: #06b6d4;
  --empty-icon: var(--text-ghost);
}

:global(:root[data-theme="light"]) {
  --bg-primary: #f8fafc;
  --bg-secondary: #ffffff;
  --bg-tertiary: #f1f5f9;
  --bg-panel: #ffffff;
  --border-color: #cbd5e1;
  --border-subtle: #94a3b8;
  --text-primary: #0f172a;
  --text-secondary: #1e293b;
  --text-muted: #475569;
  --text-dim: #64748b;
  --text-ghost: #94a3b8;
  --accent: #ea580c;
  --accent-glow: rgba(234, 88, 12, 0.2);
  --accent-hover: #f97316;
  --gradient-start: #ffffff;
  --gradient-end: #f1f5f9;
  --card-bg: #ffffff;
  --item-bg: #f8fafc;
  --btn-bg: #e2e8f0;
  --btn-hover: #cbd5e1;
  --status-online-bg: rgba(34, 197, 94, 0.08);
  --status-online-border: rgba(34, 197, 94, 0.25);
  --status-offline-bg: rgba(239, 68, 68, 0.08);
  --status-offline-border: rgba(239, 68, 68, 0.25);
  --badge-processing-bg: rgba(234, 88, 12, 0.1);
  --badge-queued-bg: rgba(139, 92, 246, 0.1);
  --header-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
  --shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.06);
  --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.08);
  --active-bg: rgba(234, 88, 12, 0.08);
  --active-box: 0 0 12px rgba(234, 88, 12, 0.15);
  --status-online-color: #059669;
  --status-online-glow: rgba(5, 150, 105, 0.15);
  --scrollbar-track: transparent;
  --scrollbar-thumb: #cbd5e1;
  --icon-purple: #7c3aed;
  --icon-red: #ef4444;
  --icon-gray: #6b7280;
  --accent-solid: #ea580c;
  --status-green: #059669;
  --icon-cyan: #0891b2;
  --empty-icon: var(--text-ghost);
}

.dashboard {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1rem;
}

/* ── Body / Global ── */
:global(body) {
  margin: 0;
  font-family: 'JetBrains Mono', 'SF Mono', 'Fira Code', 'Consolas', monospace;
  background: var(--bg-primary);
  color: var(--text-secondary);
}

/* ── Header ── */
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: linear-gradient(135deg, var(--gradient-start), var(--gradient-end));
  border: 1px solid var(--border-color);
  border-radius: 1rem;
  padding: 1rem 1.5rem;
  box-shadow: var(--header-shadow);
  gap: 1rem;
}

.settings-btn {
  display: grid;
  place-content: center;
  width: 36px;
  height: 36px;
  padding: 0.5rem;
  background: var(--btn-bg);
  border: 2px solid var(--border-color);
  border-radius: 0.5rem;
  color: var(--text-muted);
  flex-shrink: 0;
}

.settings-btn:not(:disabled):hover {
  background: var(--btn-hover);
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 0 8px var(--accent-glow);
}

.settings-btn svg {
  width: 18px;
  height: 18px;
  stroke: var(--text-muted);
}

.settings-btn:hover svg {
  stroke: var(--accent);
}

.logo-area {
  display: flex;
  align-items: center;
  gap: 0.75rem;
}

.logo-icon {
  width: 48px;
  height: 48px;
  filter: drop-shadow(0 0 8px var(--accent-glow));
}

.logo-text h1 {
  margin: 0;
  font-size: 1.75rem;
  font-weight: 800;
  letter-spacing: 0.1em;
  background: linear-gradient(135deg, var(--accent), #ea580c);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  background-clip: text;
}

.subtitle {
  font-size: 0.7rem;
  color: var(--text-dim);
  letter-spacing: 0.15em;
  text-transform: uppercase;
}

.status-bar {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.robot-mode {
  font-size: 0.75rem;
  color: var(--text-ghost);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-color);
  padding: 0.375rem 0.75rem;
  border-radius: 999px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

.status-indicator {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  padding: 0.375rem 0.875rem;
  border-radius: 999px;
}

.status-indicator.online {
  color: #22c55e;
  background: var(--status-online-bg);
  border: 1px solid var(--status-online-border);
}

.status-indicator.offline {
  color: #ef4444;
  background: var(--status-offline-bg);
  border: 1px solid var(--status-offline-border);
}

.pulse {
  width: 8px;
  height: 8px;
  background: var(--accent);
  border-radius: 50%;
  animation: pulse-anim 2s ease-in-out infinite;
}

.pulse-red {
  background: #ef4444 !important;
}

@keyframes pulse-anim {
  0%, 100% { opacity: 1; box-shadow: 0 0 0 0 rgba(249, 115, 22, 0.4); }
  50% { opacity: 0.6; box-shadow: 0 0 0 4px rgba(249, 115, 22, 0); }
}

/* ── Panels Grid ── */
.panels-grid {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  flex: 1;
}

.row-top {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
}

.panel {
  background: var(--bg-panel);
  border: 1px solid var(--border-color);
  border-radius: 1rem;
  padding: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.panel-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.1em;
}

/* ── Buttons ── */
button {
  font-family: inherit;
  border: none;
  cursor: pointer;
  transition: all 0.2s ease;
}

button:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.btn-md {
  width: 100%;
  padding: 0.625rem 1rem;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  color: #fff;
  background: linear-gradient(135deg, var(--accent), #ea580c);
  border-radius: 0.625rem;
  text-transform: uppercase;
}

.btn-md:not(:disabled):hover {
  box-shadow: 0 4px 12px var(--accent-glow);
  transform: translateY(-1px);
}

.btn-sm {
  padding: 0.375rem 0.875rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--btn-bg);
  border-radius: 0.5rem;
  align-self: flex-start;
}

.btn-sm:not(:disabled):hover {
  background: var(--btn-hover);
}

.btn-danger {
  background: linear-gradient(135deg, #ef4444, #dc2626) !important;
  color: #fff !important;
}

/* ── Block Queue ── */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  color: var(--text-ghost);
  font-size: 0.85rem;
  padding: 1.5rem 0;
}

.block-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

.block-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  background: var(--item-bg);
  border: 1px solid var(--border-color);
  border-radius: 0.625rem;
  padding: 0.625rem 0.75rem;
}

.block-number {
  width: 24px;
  height: 24px;
  display: grid;
  place-content: center;
  font-size: 0.7rem;
  font-weight: 800;
  color: var(--text-dim);
  background: var(--bg-secondary);
  border-radius: 50%;
  flex-shrink: 0;
}

.block-color-indicator {
  width: 32px;
  height: 32px;
  border-radius: 0.5rem;
  flex-shrink: 0;
  position: relative;
  box-shadow: var(--shadow-sm);
}

.processing-ring {
  position: absolute;
  inset: -3px;
  border: 2px solid #fbbf24;
  border-radius: 0.625rem;
  border-top-color: transparent;
  animation: spin 1s linear infinite;
}

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

.block-info {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  flex: 1;
  min-width: 0;
}

.block-name {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.status-badge {
  font-size: 0.625rem;
  padding: 0.125rem 0.5rem;
  border-radius: 999px;
  align-self: flex-start;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.status-badge.processing {
  color: #f59e0b;
  background: var(--badge-processing-bg);
}

.status-badge.queued {
  color: #8b5cf6;
  background: var(--badge-queued-bg);
}

.block-action {
  font-size: 0.65rem;
  color: var(--text-dim);
  text-align: right;
}

/* ── D-Pad Motion Controls ── */
.d-pad {
  display: grid;
  grid-template-areas:
    ". up ."
    "left center right"
    ". down .";
  gap: 0.5rem;
  width: 240px;
  height: 240px;
  margin: 0 auto;
}

.btn-direction {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.25rem;
  background: linear-gradient(135deg, var(--bg-secondary), var(--item-bg));
  border: 2px solid var(--border-color);
  border-radius: 0.75rem;
  color: var(--text-muted);
  padding: 0;
}

.btn-direction svg {
  width: 28px;
  height: 28px;
}

.btn-direction span {
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.btn-direction:not(:disabled):hover {
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 4px 12px var(--accent-glow);
  transform: scale(1.05);
}

.btn-direction.up { grid-area: up; }
.btn-direction.left { grid-area: left; }
.btn-direction.center {
  grid-area: center;
  background: linear-gradient(135deg, #dc2626, #991b1b);
  border-color: #dc2626;
  color: #fff;
}

.btn-direction.center svg {
  width: 32px;
  height: 32px;
  border-radius: 4px;
}

.btn-direction.center:not(:disabled):hover {
  box-shadow: 0 4px 16px rgba(220, 38, 38, 0.5);
}

.btn-direction.down { grid-area: down; }
.btn-direction.right { grid-area: right; }

.action-status {
  text-align: center;
  font-size: 0.75rem;
  color: var(--accent);
  margin: 0;
}

/* ── Eye Controls ── */
.eye-shape-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.5rem;
}

.btn-shape {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.375rem;
  padding: 0.625rem 0.375rem;
  background: var(--item-bg);
  border: 2px solid var(--border-color);
  border-radius: 0.625rem;
  color: var(--text-muted);
  cursor: pointer;
}

.btn-shape.active {
  border-color: var(--accent);
  background: var(--active-bg);
  color: var(--accent);
  box-shadow: var(--active-box);
}

.btn-shape span {
  font-size: 0.625rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.eye-preview {
  width: 80px;
  height: 40px;
  display: grid;
  place-content: center;
}

.eye-svg {
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.3));
}

.section-label {
  font-size: 0.7rem !important;
  font-weight: 600;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.15em;
  margin: 0.25rem 0 0.5rem;
}

/* ── Color Grid ── */
.color-grid {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0.375rem;
  margin-bottom: 0.75rem;
}

.color-btn {
  width: 24px;
  height: 24px;
  border-radius: 0.375rem;
  border: 2px solid var(--border-subtle);
  display: grid;
  place-content: center;
  cursor: pointer;
  position: relative;
}

.color-btn.active {
  border-color: var(--accent);
  box-shadow: var(--active-box), 0 4px 12px rgba(0, 0, 0, 0.2);
  transform: scale(1.15);
}

.color-btn.active .check-icon {
  font-size: 0.7rem;
  font-weight: 800;
  color: #fff;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
}

.action-row {
  display: flex;
  gap: 0.5rem;
}

/* ── Gesture Panel ── */
.mode-card {
  background: var(--bg-tertiary);
  border: 1px solid var(--border-color);
  border-radius: 0.625rem;
  padding: 0.75rem;
}

.mode-switch {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.5rem;
}

.btn-mode {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.375rem;
  padding: 0.5rem 0.75rem;
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--text-dim);
  background: var(--bg-secondary);
  border: 2px solid var(--border-color);
  border-radius: 0.625rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.btn-mode.active {
  color: var(--status-online-color);
  border-color: var(--status-online-color);
  background: var(--status-online-bg);
  box-shadow: 0 0 12px var(--status-online-glow);
}

.gesture-list {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  max-height: 200px;
  overflow-y: auto;
}

.gesture-item {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 0.625rem;
  background: var(--item-bg);
  border: 1px solid var(--border-color);
  border-radius: 0.5rem;
  border-left: 3px solid var(--border-color);
}

.gesture-icon {
  font-size: 1.1rem;
  width: 28px;
  text-align: center;
  flex-shrink: 0;
}

.gesture-info {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  flex: 1;
}

.gesture-name {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--text-secondary);
}

.gesture-action-text {
  font-size: 0.625rem;
  color: var(--text-dim);
}

.gesture-time {
  font-size: 0.625rem;
  color: var(--text-ghost);
  flex-shrink: 0;
}

/* ── Gesture Reference ── */
.gesture-ref {
  border-top: 1px solid var(--border-color);
  padding-top: 0.75rem;
}

.ref-toggle {
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  cursor: pointer;
  user-select: none;
}

.ref-toggle::-webkit-details-marker {
  display: none;
}

.ref-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.25rem;
  margin-top: 0.5rem;
}

.ref-item {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.65rem;
  color: var(--text-muted);
  padding: 0.25rem;
}

.ref-emoji {
  font-size: 0.9rem;
}

/* ── Toast ── */
.toast {
  position: fixed;
  bottom: 2rem;
  left: 50%;
  transform: translateX(-50%);
  padding: 0.75rem 1.5rem;
  font-size: 0.8rem;
  font-weight: 700;
  border-radius: 999px;
  z-index: 100;
  letter-spacing: 0.05em;
}

.toast.success {
  color: #fff;
  background: linear-gradient(135deg, #22c55e, #16a34a);
  box-shadow: 0 4px 16px rgba(34, 197, 94, 0.3);
}

.toast.error {
  color: #fff;
  background: linear-gradient(135deg, #ef4444, #dc2626);
  box-shadow: 0 4px 16px rgba(239, 68, 68, 0.3);
}

.toast-enter-active,
.toast-leave-active {
  transition: all 0.3s ease;
}

.toast-enter-from,
.toast-leave-to {
  opacity: 0;
  transform: translateX(-50%) translateY(1rem);
}

/* ── Scrollbar ── */
.gesture-list::-webkit-scrollbar {
  width: 4px;
}

.gesture-list::-webkit-scrollbar-track {
  background: var(--scrollbar-track);
}

.gesture-list::-webkit-scrollbar-thumb {
  background: var(--scrollbar-thumb);
  border-radius: 999px;
}

/* ── Responsive ── */
@media (max-width: 768px) {
  .panels-grid {
    flex-direction: column;
  }

  .row-top {
    grid-template-columns: 1fr;
  }

  .header {
    flex-direction: column;
    gap: 0.75rem;
  }

  .lang-selector {
    order: 0;
  }

  .d-pad {
    width: 200px;
    height: 200px;
  }

  .eye-shape-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
</style>
