<script setup lang="ts">
import { ref, computed, onBeforeUnmount, nextTick } from 'vue'
import { invoke } from '@tauri-apps/api/core'
import { Monitor, RefreshCw } from 'lucide-vue-next'
import { computeDropdownPosition } from '../utils/dropdown'

interface WindowInfo {
  exe_name: string
  exe_path: string
  title: string
  display_name: string
  icon: string | null
}

interface Props {
  excludePaths?: string[]
}

const props = withDefaults(defineProps<Props>(), {
  excludePaths: () => [],
})

const emit = defineEmits<{
  select: [app: WindowInfo]
}>()

const isOpen = ref(false)
const loading = ref(false)
const processes = ref<WindowInfo[]>([])
const searchQuery = ref('')
const highlightedIndex = ref(-1)
const triggerRef = ref<HTMLButtonElement | null>(null)
const panelRef = ref<HTMLDivElement | null>(null)
const panelStyle = ref<Record<string, string>>({})
const searchInputRef = ref<HTMLInputElement | null>(null)

const filteredProcesses = computed(() => {
  let list = processes.value.filter(
    p => !props.excludePaths.includes(p.exe_path.toLowerCase())
  )
  if (searchQuery.value) {
    const q = searchQuery.value.toLowerCase()
    list = list.filter(p =>
      p.display_name.toLowerCase().includes(q) ||
      p.exe_name.toLowerCase().includes(q)
    )
  }
  return list
})

async function fetchProcesses() {
  loading.value = true
  try {
    processes.value = await invoke<WindowInfo[]>('get_running_processes')
  } catch (e) {
    console.error('Failed to fetch running processes:', e)
  } finally {
    loading.value = false
  }
}

function updatePosition() {
  if (!triggerRef.value) return
  panelStyle.value = computeDropdownPosition(triggerRef.value, { minWidth: 280, estimatedHeight: 290, panelEl: panelRef.value })
}

function toggle() {
  if (isOpen.value) {
    close()
  } else {
    open()
  }
}

async function open() {
  isOpen.value = true
  updatePosition()
  highlightedIndex.value = 0
  searchQuery.value = ''
  nextTick(() => updatePosition())
  await fetchProcesses()
  nextTick(() => searchInputRef.value?.focus())
  document.addEventListener('click', onClickOutside, true)
  document.addEventListener('scroll', onScrollOrResize, true)
  window.addEventListener('resize', onScrollOrResize)
}

function close() {
  isOpen.value = false
  highlightedIndex.value = -1
  document.removeEventListener('click', onClickOutside, true)
  document.removeEventListener('scroll', onScrollOrResize, true)
  window.removeEventListener('resize', onScrollOrResize)
}

function selectApp(app: WindowInfo) {
  emit('select', app)
  close()
}

function onClickOutside(e: MouseEvent) {
  const target = e.target as Node
  if (triggerRef.value?.contains(target) || panelRef.value?.contains(target)) return
  close()
}

function onScrollOrResize() {
  if (isOpen.value) updatePosition()
}

function onSearchKeydown(e: KeyboardEvent) {
  switch (e.key) {
    case 'ArrowDown':
      e.preventDefault()
      highlightedIndex.value = Math.min(highlightedIndex.value + 1, filteredProcesses.value.length - 1)
      break
    case 'ArrowUp':
      e.preventDefault()
      highlightedIndex.value = Math.max(highlightedIndex.value - 1, 0)
      break
    case 'Enter':
      e.preventDefault()
      if (highlightedIndex.value >= 0 && filteredProcesses.value[highlightedIndex.value]) {
        selectApp(filteredProcesses.value[highlightedIndex.value])
      }
      break
    case 'Escape':
      e.preventDefault()
      close()
      triggerRef.value?.focus()
      break
  }
}

onBeforeUnmount(() => {
  document.removeEventListener('click', onClickOutside, true)
  document.removeEventListener('scroll', onScrollOrResize, true)
  window.removeEventListener('resize', onScrollOrResize)
})
</script>

<template>
  <div class="relative">
    <button
      ref="triggerRef"
      type="button"
      @click="toggle"
      :aria-expanded="isOpen"
      aria-haspopup="listbox"
      class="flex items-center gap-1.5 px-2.5 py-1.5 border border-border-subtle text-text-secondary text-[0.75rem] rounded-lg hover:bg-bg-elevated hover:text-text-primary transition-colors"
    >
      <Monitor class="w-3.5 h-3.5" :stroke-width="1.5" aria-hidden="true" />
      From Running Apps
    </button>

    <Teleport to="#app">
      <Transition name="dropdown">
      <div
        v-if="isOpen"
        ref="panelRef"
        :style="panelStyle"
        class="bg-bg-surface border border-border-visible rounded-xl shadow-[0_4px_24px_rgba(0,0,0,0.4)] overflow-hidden"
      >
        <div class="px-2 pt-2 pb-1 flex items-center gap-1.5">
          <input
            ref="searchInputRef"
            v-model="searchQuery"
            type="text"
            class="flex-1 px-2.5 py-1.5 bg-bg-inset border border-border-subtle rounded-lg text-[0.75rem] text-text-primary placeholder-text-tertiary focus:outline-none focus:border-border-visible"
            placeholder="Search apps..."
            aria-label="Search running applications"
            @keydown="onSearchKeydown"
          />
          <button
            type="button"
            @click="fetchProcesses"
            class="p-1.5 text-text-tertiary hover:text-text-secondary transition-colors"
            aria-label="Refresh application list"
            v-tooltip="'Refresh'"
          >
            <RefreshCw class="w-3.5 h-3.5" :class="{ 'animate-spin': loading }" :stroke-width="1.5" aria-hidden="true" />
          </button>
        </div>

        <div class="max-h-[240px] overflow-y-auto py-1" role="listbox" aria-label="Running applications">
          <div v-if="loading && filteredProcesses.length === 0" class="px-3 py-4 text-center text-[0.75rem] text-text-tertiary" aria-live="polite">
            Loading...
          </div>
          <div v-else-if="filteredProcesses.length === 0" class="px-3 py-4 text-center text-[0.75rem] text-text-tertiary">
            No apps found
          </div>
          <div
            v-for="(app, index) in filteredProcesses"
            :key="app.exe_path"
            role="option"
            :id="'app-option-' + index"
            :aria-selected="highlightedIndex === index"
            @click="selectApp(app)"
            @mouseenter="highlightedIndex = index"
            class="flex items-center gap-2.5 px-3 py-2 cursor-pointer transition-colors"
            :class="{ 'bg-bg-elevated': highlightedIndex === index }"
          >
            <img
              v-if="app.icon"
              :src="app.icon"
              class="w-5 h-5 shrink-0"
              alt=""
            />
            <div v-else class="w-5 h-5 shrink-0 rounded bg-bg-elevated" />
            <div class="flex-1 min-w-0">
              <p class="text-[0.8125rem] text-text-primary truncate">{{ app.display_name }}</p>
              <p class="text-[0.6875rem] text-text-tertiary truncate">{{ app.exe_name }}</p>
            </div>
          </div>
        </div>
      </div>
      </Transition>
    </Teleport>
  </div>
</template>
