<script setup lang="ts">
import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useSessionStore } from "../stores/session";
import { useConfigStore } from "../stores/config";
import { useLabelsStore } from "../stores/labels";
import { useUiStore } from "../stores/ui";
import Icon from "./Icon.vue";
import LabelsModal from "./LabelsModal.vue";

const route = useRoute();
const router = useRouter();
const session = useSessionStore();
const config = useConfigStore();
const labels = useLabelsStore();
const ui = useUiStore();

const managing = ref(false);
const showShortcuts = ref(false);
const searchInput = ref<HTMLInputElement | null>(null);
const searchText = ref(typeof route.query.q === "string" ? route.query.q : "");
let searchTimer: ReturnType<typeof setTimeout> | undefined;

const shortcuts = [
  { label: "New note", keys: ["c"] },
  { label: "Search", keys: ["/"] },
  { label: "Go to Board", keys: ["g", "b"] },
  { label: "Go to Graph", keys: ["g", "g"] },
  { label: "Go to Reminders", keys: ["g", "r"] },
  { label: "Save & close (editor)", keys: ["⌘/Ctrl", "Enter"] },
  { label: "Save & new (composer)", keys: ["Shift", "Enter"] },
  { label: "Dismiss / close", keys: ["Esc"] },
  { label: "This help", keys: ["?"] },
];

let gPending = false;
let gTimer: ReturnType<typeof setTimeout> | undefined;

function isTyping(e: KeyboardEvent): boolean {
  const el = e.target as HTMLElement | null;
  return !!el && (el.tagName === "INPUT" || el.tagName === "TEXTAREA" || el.isContentEditable);
}

function focusSearch() {
  searchInput.value?.focus();
  searchInput.value?.select();
}

// `c` composes: jump to the board (which autofocuses its quick-add) or, if
// already there, ask the board to reopen the composer.
function compose() {
  if (route.name !== "board") void router.push("/");
  else ui.requestCompose();
}

// Global keyboard shortcuts, active only inside the authed shell. Ignored while
// typing in a field (except Esc, which blurs it). `g` starts a two-key jump
// (g b / g g / g r) with a short timeout.
function onKeydown(e: KeyboardEvent) {
  if (isTyping(e)) {
    if (e.key === "Escape") (e.target as HTMLElement).blur();
    return;
  }
  if (e.key === "Escape" && showShortcuts.value) {
    showShortcuts.value = false;
    return;
  }
  if (e.metaKey || e.ctrlKey || e.altKey) return;
  if (gPending) {
    gPending = false;
    clearTimeout(gTimer);
    if (e.key === "b") {
      e.preventDefault();
      void router.push("/");
      return;
    }
    if (e.key === "g") {
      e.preventDefault();
      void router.push("/graph");
      return;
    }
    if (e.key === "r") {
      e.preventDefault();
      void router.push("/reminders");
      return;
    }
  }
  if (e.key === "/") {
    e.preventDefault();
    focusSearch();
    return;
  }
  if (e.key === "c") {
    e.preventDefault();
    compose();
    return;
  }
  if (e.key === "?") {
    e.preventDefault();
    showShortcuts.value = true;
    return;
  }
  if (e.key === "g") {
    gPending = true;
    clearTimeout(gTimer);
    gTimer = setTimeout(() => (gPending = false), 800);
  }
}

onMounted(() => {
  if (!labels.loaded) void labels.load();
  window.addEventListener("keydown", onKeydown);
});
onBeforeUnmount(() => window.removeEventListener("keydown", onKeydown));

const currentLabelId = computed(() => (route.name === "label" ? String(route.params.id) : null));

function onSearch(value: string) {
  searchText.value = value;
  clearTimeout(searchTimer);
  searchTimer = setTimeout(() => {
    const q = searchText.value.trim();
    if (q) router.push({ name: "search", query: { q } });
    else if (route.name === "search") router.push("/");
  }, 250);
}

// Clear the search box when navigating to a non-search view.
watch(
  () => route.name,
  (name) => {
    if (name !== "search") searchText.value = "";
  },
);

async function signOut() {
  await session.logout();
  await router.replace("/login");
}
</script>

<template>
  <div class="flex min-h-full flex-col">
    <header
      class="sticky top-0 z-20 border-b border-neutral-200 bg-neutral-50/90 backdrop-blur dark:border-neutral-800 dark:bg-neutral-950/90"
    >
      <div class="flex items-center gap-3 px-4 py-3">
        <div class="flex shrink-0 items-center gap-2">
          <div class="flex h-8 w-8 items-center justify-center rounded-lg bg-brand text-sm font-black text-neutral-900">
            TS
          </div>
          <span class="hidden font-semibold sm:inline">{{ config.siteName }}</span>
        </div>

        <div class="flex flex-1 justify-center">
          <input
            ref="searchInput"
            :value="searchText"
            type="search"
            placeholder="Search notes…"
            aria-label="Search notes"
            class="w-full max-w-md rounded-lg border border-neutral-300 bg-white px-3 py-1.5 text-sm outline-none focus-visible:ring-2 focus-visible:ring-brand dark:border-neutral-700 dark:bg-neutral-900"
            @input="onSearch(($event.target as HTMLInputElement).value)"
          />
        </div>

        <div class="flex shrink-0 items-center gap-3">
          <span class="hidden text-sm text-neutral-500 md:inline dark:text-neutral-400">{{
            session.user?.display_name
          }}</span>
          <RouterLink
            v-if="session.user?.is_admin"
            to="/settings"
            class="icon-btn"
            title="Settings"
            aria-label="Settings"
          >
            <Icon name="settings" />
          </RouterLink>
          <button type="button" class="icon-btn" title="Sign out" aria-label="Sign out" @click="signOut">
            <Icon name="logout" />
          </button>
        </div>
      </div>
    </header>

    <div class="flex flex-1">
      <aside class="hidden w-56 shrink-0 border-r border-neutral-200 p-3 sm:block dark:border-neutral-800">
        <nav class="flex flex-col gap-0.5 text-sm">
          <RouterLink to="/" class="nav-link" :class="route.name === 'board' ? 'nav-link-active' : ''">
            <Icon name="note" /> Notes
          </RouterLink>
          <RouterLink to="/graph" class="nav-link" :class="route.name === 'graph' ? 'nav-link-active' : ''">
            <Icon name="graph" /> Graph
          </RouterLink>

          <div class="mt-3 flex items-center justify-between px-3 pb-1">
            <span class="text-xs font-semibold uppercase tracking-wide text-neutral-400">Labels</span>
            <button
              type="button"
              class="text-neutral-400 hover:text-neutral-600 dark:hover:text-neutral-200"
              title="Edit labels"
              aria-label="Edit labels"
              @click="managing = true"
            >
              <Icon name="pencil" />
            </button>
          </div>
          <p v-if="!labels.items.length" class="px-3 py-1 text-xs text-neutral-400">No labels yet</p>
          <RouterLink
            v-for="lb in labels.items"
            :key="lb.id"
            :to="`/label/${lb.id}`"
            class="nav-link"
            :class="currentLabelId === lb.id ? 'nav-link-active' : ''"
          >
            <Icon name="tag" /> <span class="truncate">{{ lb.name }}</span>
          </RouterLink>

          <RouterLink to="/archive" class="nav-link mt-3" :class="route.name === 'archive' ? 'nav-link-active' : ''">
            <Icon name="archive" /> Archive
          </RouterLink>
          <RouterLink to="/trash" class="nav-link" :class="route.name === 'trash' ? 'nav-link-active' : ''">
            <Icon name="trash" /> Trash
          </RouterLink>
          <RouterLink
            to="/reminders"
            class="nav-link"
            :class="route.name === 'reminders' ? 'nav-link-active' : ''"
          >
            <Icon name="bell" /> Reminders
          </RouterLink>
        </nav>
      </aside>

      <main class="min-w-0 flex-1"><RouterView /></main>
    </div>

    <LabelsModal v-if="managing" @close="managing = false" />

    <div
      v-if="showShortcuts"
      class="fixed inset-0 z-50 flex items-center justify-center bg-black/40 p-4"
      @click.self="showShortcuts = false"
    >
      <div
        class="w-full max-w-sm rounded-xl border border-neutral-200 bg-white p-5 shadow-xl dark:border-neutral-700 dark:bg-neutral-900"
        role="dialog"
        aria-modal="true"
        aria-label="Keyboard shortcuts"
      >
        <div class="mb-3 flex items-center justify-between">
          <h2 class="text-sm font-semibold">Keyboard shortcuts</h2>
          <button
            type="button"
            class="text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-100"
            aria-label="Close"
            @click="showShortcuts = false"
          >
            ×
          </button>
        </div>
        <dl class="flex flex-col gap-2 text-sm">
          <div v-for="s in shortcuts" :key="s.label" class="flex items-center justify-between gap-4">
            <dt class="text-neutral-600 dark:text-neutral-300">{{ s.label }}</dt>
            <dd class="flex items-center gap-1">
              <kbd
                v-for="(k, i) in s.keys"
                :key="i"
                class="rounded border border-neutral-300 bg-neutral-100 px-1.5 py-0.5 font-mono text-xs text-neutral-700 dark:border-neutral-600 dark:bg-neutral-800 dark:text-neutral-200"
                >{{ k }}</kbd
              >
            </dd>
          </div>
        </dl>
      </div>
    </div>
  </div>
</template>
