<template>
  <Teleport to="body">
    <div
      v-if="visible"
      class="fixed inset-0 z-50 flex items-start justify-center pt-[15vh]"
      @click.self="close"
      @keydown.esc="close"
    >
      <!-- Backdrop -->
      <div class="absolute inset-0 bg-black/50" @click="close" />

      <!-- Palette -->
      <div
        ref="paletteRef"
        class="relative w-full max-w-lg bg-[#161b22] border border-[#30363d] rounded-lg shadow-2xl overflow-hidden animate-fade-in"
      >
        <!-- Search input -->
        <div class="flex items-center px-4 py-3 border-b border-[#30363d]">
          <svg class="w-4 h-4 text-[var(--wraith-text-muted)] mr-3 shrink-0" viewBox="0 0 16 16" fill="currentColor">
            <path d="M11.5 7a4.5 4.5 0 1 1-9 0 4.5 4.5 0 0 1 9 0zm-.82 4.74a6 6 0 1 1 1.06-1.06l3.04 3.04a.75.75 0 1 1-1.06 1.06l-3.04-3.04z" />
          </svg>
          <input
            ref="inputRef"
            v-model="query"
            type="text"
            placeholder="Search connections, actions..."
            class="flex-1 bg-transparent text-sm text-[var(--wraith-text-primary)] placeholder-[var(--wraith-text-muted)] outline-none"
            @keydown.down.prevent="moveSelection(1)"
            @keydown.up.prevent="moveSelection(-1)"
            @keydown.enter.prevent="executeSelected"
          />
          <kbd class="ml-2 px-1.5 py-0.5 text-[10px] text-[var(--wraith-text-muted)] bg-[#30363d] rounded border border-[#484f58]">ESC</kbd>
        </div>

        <!-- Results -->
        <div class="max-h-80 overflow-y-auto py-1">
          <!-- Connections group -->
          <template v-if="filteredConnections.length > 0">
            <div class="px-4 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-[var(--wraith-text-muted)]">
              Connections
            </div>
            <button
              v-for="(conn, idx) in filteredConnections"
              :key="`conn-${conn.id}`"
              class="w-full flex items-center gap-3 px-4 py-2 text-sm text-left transition-colors cursor-pointer"
              :class="selectedIndex === idx ? 'bg-[#1f6feb]/20 text-[var(--wraith-text-primary)]' : 'text-[var(--wraith-text-secondary)] hover:bg-[#30363d]'"
              @click="connectTo(conn)"
              @mouseenter="selectedIndex = idx"
            >
              <span
                class="w-2 h-2 rounded-full shrink-0"
                :class="conn.protocol === 'ssh' ? 'bg-[#3fb950]' : 'bg-[#1f6feb]'"
              />
              <span class="flex-1 truncate">{{ conn.name }}</span>
              <span class="text-xs text-[var(--wraith-text-muted)]">{{ conn.hostname }}</span>
              <span class="text-[10px] px-1.5 py-0.5 rounded bg-[#30363d] text-[var(--wraith-text-muted)] uppercase">
                {{ conn.protocol }}
              </span>
            </button>
          </template>

          <!-- Actions group -->
          <template v-if="filteredActions.length > 0">
            <div class="px-4 py-1.5 text-[10px] font-semibold uppercase tracking-wider text-[var(--wraith-text-muted)]">
              Actions
            </div>
            <button
              v-for="(action, idx) in filteredActions"
              :key="`action-${action.id}`"
              class="w-full flex items-center gap-3 px-4 py-2 text-sm text-left transition-colors cursor-pointer"
              :class="selectedIndex === filteredConnections.length + idx ? 'bg-[#1f6feb]/20 text-[var(--wraith-text-primary)]' : 'text-[var(--wraith-text-secondary)] hover:bg-[#30363d]'"
              @click="executeAction(action)"
              @mouseenter="selectedIndex = filteredConnections.length + idx"
            >
              <span class="w-4 h-4 flex items-center justify-center text-[var(--wraith-text-muted)] shrink-0" v-html="action.icon" />
              <span class="flex-1 truncate">{{ action.label }}</span>
              <span v-if="action.shortcut" class="text-[10px] px-1.5 py-0.5 rounded bg-[#30363d] text-[var(--wraith-text-muted)]">
                {{ action.shortcut }}
              </span>
            </button>
          </template>

          <!-- No results -->
          <div
            v-if="filteredConnections.length === 0 && filteredActions.length === 0"
            class="px-4 py-8 text-center text-sm text-[var(--wraith-text-muted)]"
          >
            No results found
          </div>
        </div>
      </div>
    </div>
  </Teleport>
</template>

<script setup lang="ts">
import { ref, computed, watch, nextTick } from "vue";
import { useConnectionStore, type Connection } from "@/stores/connection.store";
import { useSessionStore } from "@/stores/session.store";

interface PaletteAction {
  id: string;
  label: string;
  icon: string;
  shortcut?: string;
  handler: () => void;
}

const visible = ref(false);
const query = ref("");
const selectedIndex = ref(0);
const inputRef = ref<HTMLInputElement | null>(null);
const paletteRef = ref<HTMLDivElement | null>(null);

const connectionStore = useConnectionStore();
const sessionStore = useSessionStore();

const emit = defineEmits<{
  (e: "open-import"): void;
  (e: "open-settings"): void;
  (e: "open-new-connection", protocol?: "ssh" | "rdp"): void;
}>();

const actions: PaletteAction[] = [
  {
    id: "new-ssh",
    label: "New SSH Connection",
    icon: `<svg viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4"><path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0 1 14.25 15H1.75A1.75 1.75 0 0 1 0 13.25Zm1.75-.25a.25.25 0 0 0-.25.25v10.5c0 .138.112.25.25.25h12.5a.25.25 0 0 0 .25-.25V2.75a.25.25 0 0 0-.25-.25ZM7.25 8a.749.749 0 0 1-.22.53l-2.25 2.25a.749.749 0 1 1-1.06-1.06L5.44 8 3.72 6.28a.749.749 0 1 1 1.06-1.06l2.25 2.25c.141.14.22.331.22.53Zm1.5 1.5h3a.75.75 0 0 1 0 1.5h-3a.75.75 0 0 1 0-1.5Z"/></svg>`,
    handler: () => {
      emit("open-new-connection", "ssh");
      close();
    },
  },
  {
    id: "new-rdp",
    label: "New RDP Connection",
    icon: `<svg viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4"><path d="M1.75 2.5h12.5a.25.25 0 0 1 .25.25v7.5a.25.25 0 0 1-.25.25H1.75a.25.25 0 0 1-.25-.25v-7.5a.25.25 0 0 1 .25-.25ZM14.25 1H1.75A1.75 1.75 0 0 0 0 2.75v7.5C0 11.216.784 12 1.75 12h4.388l-.533 1.5H4a.75.75 0 0 0 0 1.5h8a.75.75 0 0 0 0-1.5h-1.605l-.533-1.5h4.388A1.75 1.75 0 0 0 16 10.25v-7.5A1.75 1.75 0 0 0 14.25 1ZM9.112 13.5H6.888l.533-1.5h1.158l.533 1.5Z"/></svg>`,
    handler: () => {
      emit("open-new-connection", "rdp");
      close();
    },
  },
  {
    id: "open-vault",
    label: "Open Vault",
    icon: `<svg viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4"><path d="M4 4v2h-.25A1.75 1.75 0 0 0 2 7.75v5.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0 0 14 13.25v-5.5A1.75 1.75 0 0 0 12.25 6H12V4a4 4 0 1 0-8 0Zm6.5 2V4a2.5 2.5 0 0 0-5 0v2ZM8 9.5a1.5 1.5 0 0 1 .5 2.915V13.5a.5.5 0 0 1-1 0v-1.085A1.5 1.5 0 0 1 8 9.5Z"/></svg>`,
    handler: () => {
      // TODO: Navigate to vault
      close();
    },
  },
  {
    id: "settings",
    label: "Settings",
    icon: `<svg viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4"><path d="M8 0a8.2 8.2 0 0 1 .701.031C8.955.017 9.209 0 9.466 0a1.934 1.934 0 0 1 1.466.665c.33.367.51.831.54 1.316a7.96 7.96 0 0 1 .82.4c.463-.207.97-.29 1.476-.19.504.1.963.37 1.3.77.339.404.516.91.5 1.423a1.94 1.94 0 0 1-.405 1.168 8.02 8.02 0 0 1 .356.9 1.939 1.939 0 0 1 1.48.803 1.941 1.941 0 0 1 0 2.29 1.939 1.939 0 0 1-1.48.803c-.095.316-.215.622-.357.9a1.94 1.94 0 0 1-.094 2.59 1.94 1.94 0 0 1-2.776.22 7.96 7.96 0 0 1-.82.4 1.94 1.94 0 0 1-2.006 1.98A8.2 8.2 0 0 1 8 16a8.2 8.2 0 0 1-.701-.031 1.938 1.938 0 0 1-2.005-1.98 7.96 7.96 0 0 1-.82-.4 1.94 1.94 0 0 1-2.776-.22 1.94 1.94 0 0 1-.094-2.59 8.02 8.02 0 0 1-.357-.9A1.939 1.939 0 0 1 0 8.945a1.941 1.941 0 0 1 0-2.29 1.939 1.939 0 0 1 1.247-.803c.095-.316.215-.622.357-.9a1.94 1.94 0 0 1 .094-2.59 1.94 1.94 0 0 1 2.776-.22c.258-.157.532-.293.82-.4A1.934 1.934 0 0 1 6.834.665 1.934 1.934 0 0 1 8.3.03 8.2 8.2 0 0 1 8 0ZM8 5a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z"/></svg>`,
    handler: () => {
      emit("open-settings");
      close();
    },
  },
  {
    id: "import-moba",
    label: "Import MobaXTerm Config",
    icon: `<svg viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4"><path d="M2.75 14A1.75 1.75 0 0 1 1 12.25v-2.5a.75.75 0 0 1 1.5 0v2.5c0 .138.112.25.25.25h10.5a.25.25 0 0 0 .25-.25v-2.5a.75.75 0 0 1 1.5 0v2.5A1.75 1.75 0 0 1 13.25 14ZM11.78 4.72a.749.749 0 1 1-1.06 1.06L8.75 3.81V9.5a.75.75 0 0 1-1.5 0V3.81L5.28 5.78a.749.749 0 1 1-1.06-1.06l3.25-3.25a.749.749 0 0 1 1.06 0l3.25 3.25Z"/></svg>`,
    handler: () => {
      emit("open-import");
      close();
    },
  },
];

/** Fuzzy match: checks if all characters of needle appear in order in haystack. */
function fuzzyMatch(needle: string, haystack: string): boolean {
  const n = needle.toLowerCase();
  const h = haystack.toLowerCase();
  let ni = 0;
  for (let hi = 0; hi < h.length && ni < n.length; hi++) {
    if (h[hi] === n[ni]) ni++;
  }
  return ni === n.length;
}

const filteredConnections = computed(() => {
  const q = query.value.trim();
  if (!q) return connectionStore.connections.slice(0, 10);
  return connectionStore.connections.filter(
    (c) =>
      fuzzyMatch(q, c.name) ||
      fuzzyMatch(q, c.hostname) ||
      c.tags?.some((t) => fuzzyMatch(q, t)),
  );
});

const filteredActions = computed(() => {
  const q = query.value.trim();
  if (!q) return actions;
  return actions.filter((a) => fuzzyMatch(q, a.label));
});

const totalItems = computed(() => filteredConnections.value.length + filteredActions.value.length);

function moveSelection(delta: number): void {
  if (totalItems.value === 0) return;
  selectedIndex.value = (selectedIndex.value + delta + totalItems.value) % totalItems.value;
}

function executeSelected(): void {
  if (totalItems.value === 0) return;
  const idx = selectedIndex.value;
  if (idx < filteredConnections.value.length) {
    connectTo(filteredConnections.value[idx]);
  } else {
    const actionIdx = idx - filteredConnections.value.length;
    executeAction(filteredActions.value[actionIdx]);
  }
}

function connectTo(conn: Connection): void {
  sessionStore.connect(conn.id);
  close();
}

function executeAction(action: PaletteAction): void {
  action.handler();
}

function open(): void {
  visible.value = true;
  query.value = "";
  selectedIndex.value = 0;
  nextTick(() => inputRef.value?.focus());
}

function close(): void {
  visible.value = false;
}

function toggle(): void {
  if (visible.value) {
    close();
  } else {
    open();
  }
}

// Reset selection when query changes
watch(query, () => {
  selectedIndex.value = 0;
});

defineExpose({ open, close, toggle, visible });
</script>

<style scoped>
.animate-fade-in {
  animation: palette-fade-in 0.1s ease-out;
}

@keyframes palette-fade-in {
  from {
    opacity: 0;
    transform: translateY(-8px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
</style>
