<script setup lang="ts">
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from "vue";
import { api } from "../api/client";
import { useNotesStore } from "../stores/notes";
import { useTitlesStore, type TitleEntry } from "../stores/titles";
import ColorPicker from "./ColorPicker.vue";
import Icon from "./Icon.vue";
import LabelPicker from "./LabelPicker.vue";
import NoteChecklist from "./NoteChecklist.vue";
import { fromLocalInput, toLocalInput } from "../notes/datetime";
import type { Note, NoteLabel, NoteRevision } from "../stores/notes";
import { LABEL_CHIP_CLASSES, type NoteColor } from "../notes/colors";

// One editor for BOTH composing and editing. `inline` renders the board composer
// frame (collapsible, in-flow); the default renders the modal editor. `note` = the
// note being edited, or null to compose a new one. In compose mode there is no note
// id until a draft is persisted — on commit-with-content, or on the first rich action
// (label / image / reminder / checklist) — so we never litter empty notes.
const props = withDefaults(defineProps<{ note?: Note | null; inline?: boolean; autofocus?: boolean }>(), {
  note: null,
  inline: false,
  autofocus: false,
});
const emit = defineEmits<{ (e: "close"): void; (e: "navigate", id: string): void }>();
const notes = useNotesStore();
const titles = useTitlesStore();

const noteId = ref<string | null>(props.note?.id ?? null);
const title = ref(props.note?.title ?? "");
const body = ref(props.note?.body ?? "");
const color = ref<NoteColor>(props.note?.color ?? "default");
const labelList = ref<NoteLabel[]>(props.note ? [...props.note.labels] : []);
const createKind = ref<"text" | "list">("text"); // compose-only list toggle
const expanded = ref(!props.inline); // modal is always open; inline starts collapsed
const saving = ref(false);
const root = ref<HTMLElement | null>(null);
const bodyInput = ref<HTMLTextAreaElement | null>(null);
const fileInput = ref<HTMLInputElement | null>(null);
const uploadError = ref("");
const backlinks = ref<{ id: string; title: string }[]>([]);

// Baseline for edit-mode change detection (save only when text actually changed).
const baseline = ref<{ title: string | null; body: string; color: NoteColor }>({
  title: props.note?.title ?? null,
  body: props.note?.body ?? "",
  color: (props.note?.color ?? "default") as NoteColor,
});

const isCreate = computed(() => noteId.value === null);
const hasContent = computed(() => title.value.trim() !== "" || body.value.trim() !== "");
// Rich features need a saved note; in compose they light up once there's content.
const richEnabled = computed(() => !isCreate.value || hasContent.value);

// A synthetic note for compose mode (before anything is persisted), so the shared
// template can read attachments/items/kind/remind_at uniformly.
const draftNote = computed<Note>(() => ({
  id: "",
  title: title.value.trim() || null,
  display_title: "",
  body: body.value,
  color: color.value,
  kind: createKind.value,
  position: 0,
  pinned: false,
  archived: false,
  trashed: false,
  remind_at: null,
  labels: labelList.value,
  items: [],
  attachments: [],
  created_at: null,
  updated_at: null,
}));
const liveNote = computed<Note>(() =>
  noteId.value
    ? (notes.items.find((n) => n.id === noteId.value) ?? props.note ?? draftNote.value)
    : draftNote.value,
);
// Only edit-mode list notes render the interactive checklist; compose-list types
// lines into the textarea (they become items on create).
const showChecklist = computed(() => !isCreate.value && liveNote.value.kind === "list");
const isListMode = computed(() => (isCreate.value ? createKind.value === "list" : liveNote.value.kind === "list"));
const bodyPlaceholder = computed(() =>
  isCreate.value && createKind.value === "list" ? "One item per line…" : "Take a note…    ([[ to link a note)",
);

// Keep local state in sync when the edited note changes (modal reused for another note).
watch(
  () => props.note,
  (n) => {
    noteId.value = n?.id ?? null;
    title.value = n?.title ?? "";
    body.value = n?.body ?? "";
    color.value = (n?.color ?? "default") as NoteColor;
    labelList.value = n ? [...n.labels] : [];
    baseline.value = { title: n?.title ?? null, body: n?.body ?? "", color: (n?.color ?? "default") as NoteColor };
  },
);

// ---- persistence ----
async function createFromFields(): Promise<void> {
  let created: Note;
  if (createKind.value === "list") {
    const items = body.value
      .split("\n")
      .map((l) => l.trim())
      .filter(Boolean);
    created = await notes.create({ title: title.value, body: "", color: color.value, kind: "list", items });
    body.value = ""; // the lines moved into checklist items
  } else {
    created = await notes.create({ title: title.value, body: body.value, color: color.value });
  }
  noteId.value = created.id;
  baseline.value = { title: created.title, body: created.body, color: created.color as NoteColor };
}

// Ensure a persisted note exists (for rich actions mid-compose). Returns its id, or
// null when there's nothing to create yet (empty compose — callers just no-op).
async function ensureDraft(): Promise<string | null> {
  if (noteId.value) return noteId.value;
  if (!hasContent.value) return null;
  await createFromFields();
  return noteId.value;
}

// Persist current text fields: create in compose, patch in edit.
async function flush(): Promise<void> {
  if (saving.value) return;
  if (isCreate.value) {
    if (!hasContent.value) return;
    saving.value = true;
    try {
      await createFromFields();
    } finally {
      saving.value = false;
    }
    return;
  }
  const b = baseline.value;
  const nextBody = showChecklist.value ? b.body : body.value;
  const changed = (title.value.trim() || null) !== b.title || nextBody !== b.body || color.value !== b.color;
  if (!changed) return;
  saving.value = true;
  try {
    await notes.saveEdit(noteId.value as string, { title: title.value, body: nextBody, color: color.value });
    baseline.value = { title: title.value.trim() || null, body: nextBody, color: color.value };
  } finally {
    saving.value = false;
  }
}

function resetCompose(): void {
  noteId.value = null;
  title.value = "";
  body.value = "";
  color.value = "default";
  labelList.value = [];
  createKind.value = "text";
  baseline.value = { title: null, body: "", color: "default" };
  linkMenu.value = false;
  uploadError.value = "";
}

// ---- inline (compose) frame ----
async function open(): Promise<void> {
  expanded.value = true;
  await nextTick();
  bodyInput.value?.focus();
  autoGrow();
}
async function commitInline(): Promise<void> {
  await flush();
  resetCompose();
  expanded.value = false;
}
async function commitAndContinue(): Promise<void> {
  if (isCreate.value && !hasContent.value) return;
  await flush();
  resetCompose();
  await nextTick();
  bodyInput.value?.focus();
  autoGrow();
}

// ---- modal (edit) frame ----
async function close(): Promise<void> {
  await flush();
  emit("close");
}
function onEsc(): void {
  if (props.inline) void commitInline();
  else void close();
}
function onMetaEnter(): void {
  // Ctrl/Cmd+Enter = finish & close, in both frames (matches email/chat "send").
  if (props.inline) void commitInline();
  else void close();
}
function onBackdropMousedown(): void {
  if (!props.inline) void close();
}
function onDocClick(e: MouseEvent): void {
  // Commit the inline composer on an OUTSIDE *click* (bubble phase, not mousedown):
  // the clicked target's own handler fires first, so clicking another card's toolbar
  // performs its action, THEN the composer collapses — no wasted first click.
  if (props.inline && expanded.value && root.value && !root.value.contains(e.target as Node)) {
    void commitInline();
  }
}

// Grow the composer textarea to fit its content (inline only; modal uses fixed rows).
function autoGrow(): void {
  if (!props.inline) return;
  const el = bodyInput.value;
  if (!el) return;
  el.style.height = "auto";
  el.style.height = `${el.scrollHeight}px`;
}

async function loadBacklinks(): Promise<void> {
  if (!noteId.value) {
    backlinks.value = [];
    return;
  }
  try {
    const res = await api.get<{ backlinks: { id: string; title: string }[] }>(`/api/notes/${noteId.value}/backlinks`);
    backlinks.value = res.backlinks;
  } catch {
    backlinks.value = [];
  }
}
watch(() => noteId.value, loadBacklinks);

onMounted(async () => {
  void titles.load();
  void loadBacklinks();
  if (props.inline) {
    document.addEventListener("click", onDocClick);
    if (props.autofocus) void open();
  } else {
    await nextTick();
    bodyInput.value?.focus();
  }
});
onBeforeUnmount(() => document.removeEventListener("click", onDocClick));

// ---- outgoing links (edit mode) ----
const outgoingLinks = computed(() => {
  const re = /\[\[([^[\]]+)\]\]/g;
  const seen = new Set<string>();
  const out: { title: string; id: string | null }[] = [];
  let match: RegExpExecArray | null;
  while ((match = re.exec(body.value)) !== null) {
    const t = match[1].trim();
    const key = t.toLowerCase();
    if (t && !seen.has(key)) {
      seen.add(key);
      out.push({ title: t, id: titles.resolve(t)?.id ?? null });
    }
  }
  return out;
});

async function openLink(link: { title: string; id: string | null }) {
  if (link.id) {
    emit("navigate", link.id);
    return;
  }
  const created = await notes.createTitled(link.title);
  await titles.reload();
  emit("navigate", created.id);
}

// ---- [[ link autocomplete in the body textarea ----
const linkMenu = ref(false);
const linkQuery = ref("");
const linkStart = ref(-1);
const linkSelected = ref(0);
const linkMatches = ref<TitleEntry[]>([]);
let linkTimer: ReturnType<typeof setTimeout> | undefined;

function refreshLinkMatches() {
  if (linkTimer) clearTimeout(linkTimer);
  const q = linkQuery.value.trim();
  linkTimer = setTimeout(async () => {
    try {
      const res = await api.get<{ results: TitleEntry[] }>(`/api/notes/link-search?q=${encodeURIComponent(q)}`);
      linkMatches.value = res.results.filter((r) => r.id !== noteId.value).slice(0, 8);
    } catch {
      linkMatches.value = [];
    }
    linkSelected.value = 0;
  }, 120);
}

function onBodyInput() {
  if (props.inline) autoGrow();
  const el = bodyInput.value;
  if (!el) return;
  const caret = el.selectionStart ?? 0;
  const text = body.value.slice(0, caret);
  const open = text.lastIndexOf("[[");
  if (open === -1) {
    linkMenu.value = false;
    return;
  }
  const between = text.slice(open + 2);
  if (between.includes("]") || between.includes("\n")) {
    linkMenu.value = false;
    return;
  }
  linkQuery.value = between;
  linkStart.value = open;
  linkSelected.value = 0;
  linkMenu.value = true;
  refreshLinkMatches();
}

function insertLink(t: string) {
  const el = bodyInput.value;
  const caret = el?.selectionStart ?? body.value.length;
  const before = body.value.slice(0, linkStart.value);
  const after = body.value.slice(caret);
  const insertion = `[[${t}]]`;
  body.value = before + insertion + after;
  linkMenu.value = false;
  const pos = before.length + insertion.length;
  void nextTick(() => {
    el?.focus();
    el?.setSelectionRange(pos, pos);
  });
}

function onBodyKeydown(e: KeyboardEvent) {
  // Compose: Shift+Enter saves the note and starts a fresh one (rapid capture).
  if (props.inline && e.key === "Enter" && e.shiftKey) {
    e.preventDefault();
    void commitAndContinue();
    return;
  }
  if (!linkMenu.value || linkMatches.value.length === 0) return;
  if (e.key === "ArrowDown") {
    e.preventDefault();
    linkSelected.value = Math.min(linkSelected.value + 1, linkMatches.value.length - 1);
  } else if (e.key === "ArrowUp") {
    e.preventDefault();
    linkSelected.value = Math.max(linkSelected.value - 1, 0);
  } else if (e.key === "Enter" || e.key === "Tab") {
    const m = linkMatches.value[linkSelected.value];
    if (m) {
      e.preventDefault();
      insertLink(m.title);
    }
  } else if (e.key === "Escape") {
    // Close only the menu — don't let Esc bubble to the frame's close/commit.
    e.preventDefault();
    e.stopPropagation();
    linkMenu.value = false;
  }
}

function onTitleEnter(e: KeyboardEvent) {
  if (!props.inline) return;
  e.preventDefault();
  if (e.shiftKey) void commitAndContinue();
  else bodyInput.value?.focus();
}

// ---- reminder ----
const reminderLocal = computed(() => toLocalInput(liveNote.value.remind_at));
async function onReminderChange(e: Event) {
  const id = await ensureDraft();
  if (!id) return;
  await notes.setReminder(id, fromLocalInput((e.target as HTMLInputElement).value));
}

// ---- labels ----
async function onLabelsChange(next: NoteLabel[]) {
  const id = await ensureDraft();
  if (!id) return;
  // Tag-sourced labels are governed by the note body, not the picker — always keep
  // them so a picker save can't strip a label the #tag still mandates.
  const tagLabels = labelList.value.filter((lb) => lb.via_tag);
  const tagIds = new Set(tagLabels.map((lb) => lb.id));
  const merged = [...next.filter((lb) => !tagIds.has(lb.id)), ...tagLabels];
  labelList.value = merged;
  await notes.setLabels(
    id,
    merged.map((lb) => lb.id),
  );
}
async function removeLabel(id: string) {
  await onLabelsChange(labelList.value.filter((lb) => lb.id !== id));
}
function labelChip(c: string): string {
  return LABEL_CHIP_CLASSES[c as NoteColor] ?? LABEL_CHIP_CLASSES.default;
}

// ---- kind toggle: compose = local flag, edit = convert the existing note ----
async function toggleKind() {
  if (isCreate.value) {
    createKind.value = createKind.value === "list" ? "text" : "list";
    bodyInput.value?.focus();
    void nextTick(autoGrow);
    return;
  }
  const id = noteId.value as string;
  if (liveNote.value.kind === "list") {
    await notes.setKind(id, "text");
    return;
  }
  const lines = body.value
    .split("\n")
    .map((s) => s.trim())
    .filter((s) => s.length > 0);
  for (const line of lines) await notes.addItem(id, line);
  if (lines.length > 0) {
    body.value = "";
    await notes.saveEdit(id, { title: title.value, body: "", color: color.value });
    baseline.value = { title: title.value.trim() || null, body: "", color: color.value };
  }
  await notes.setKind(id, "list");
}

// ---- attachments ----
function pickImage() {
  fileInput.value?.click();
}
async function uploadFile(file: File) {
  const id = await ensureDraft();
  if (!id) return;
  uploadError.value = "";
  try {
    await notes.uploadAttachment(id, file);
  } catch (e) {
    uploadError.value = (e as { error?: string }).error ?? "Could not upload image.";
  }
}
async function onFileChange(e: Event) {
  const input = e.target as HTMLInputElement;
  const file = input.files?.[0];
  if (file) await uploadFile(file);
  input.value = "";
}
async function onPaste(e: ClipboardEvent) {
  const item = Array.from(e.clipboardData?.items ?? []).find((i) => i.type.startsWith("image/"));
  const file = item?.getAsFile();
  if (file) {
    e.preventDefault();
    await uploadFile(file);
  }
}

// ---- edit-mode lifecycle actions (pin/archive/trash/restore/delete) ----
async function act(fn: () => Promise<void>) {
  await fn();
  emit("close");
}

// ---- version history (modal edit only) ----
const showHistory = ref(false);
const revisions = ref<NoteRevision[]>([]);

async function loadRevisions() {
  if (!noteId.value) {
    revisions.value = [];
    return;
  }
  try {
    revisions.value = await notes.fetchRevisions(noteId.value);
  } catch {
    revisions.value = [];
  }
}
function toggleHistory() {
  showHistory.value = !showHistory.value;
  if (showHistory.value) void loadRevisions();
}
async function restoreRevisionAt(revId: string) {
  const id = noteId.value;
  if (!id) return;
  const updated = await notes.restoreRevision(id, revId);
  title.value = updated.title ?? "";
  body.value = updated.body;
  color.value = updated.color;
  baseline.value = { title: updated.title, body: updated.body, color: updated.color };
  void loadRevisions(); // the pre-restore state became a new revision
}
function revLabel(iso: string | null): string {
  if (!iso) return "";
  return new Date(iso).toLocaleString(undefined, { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" });
}
function revPreview(rev: NoteRevision): string {
  const t = (rev.title ?? "").trim();
  const b = rev.body.trim().replace(/\s+/g, " ");
  const s = t && b ? `${t} — ${b}` : t || b;
  if (!s) return "(empty)";
  return s.length > 80 ? `${s.slice(0, 80)}…` : s;
}

defineExpose({ open });
</script>

<template>
  <div
    ref="root"
    :class="
      inline
        ? 'mx-auto w-full max-w-xl'
        : 'fixed inset-0 z-40 flex items-start justify-center overflow-y-auto bg-black/40 p-4 pt-[10vh]'
    "
    @mousedown.self="onBackdropMousedown"
  >
    <!-- Collapsed composer (inline, board) -->
    <div
      v-if="inline && !expanded"
      class="rounded-xl border border-neutral-200 bg-white shadow-sm dark:border-neutral-700 dark:bg-neutral-900"
    >
      <button
        type="button"
        class="w-full rounded-xl px-4 py-3 text-left text-sm text-neutral-500 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand dark:text-neutral-400"
        @click="open"
      >
        Take a note…
      </button>
    </div>

    <!-- Shared editor card: compose (expanded, inline) OR edit (modal) -->
    <div
      v-else
      :class="
        inline
          ? 'w-full rounded-xl border border-neutral-200 bg-white shadow-sm dark:border-neutral-700 dark:bg-neutral-900'
          : 'w-full max-w-lg rounded-xl border border-neutral-200 bg-white shadow-xl dark:border-neutral-700 dark:bg-neutral-900'
      "
      :role="inline ? undefined : 'dialog'"
      :aria-modal="inline ? undefined : 'true'"
      @keydown.esc.stop="onEsc"
      @keydown.enter.meta.prevent="onMetaEnter"
      @keydown.enter.ctrl.prevent="onMetaEnter"
      @paste="onPaste"
    >
      <div class="flex flex-col gap-2 p-4">
        <div v-if="liveNote.attachments.length" class="flex flex-wrap gap-2">
          <div v-for="att in liveNote.attachments" :key="att.id" class="group/att relative">
            <img :src="att.url" alt="" loading="lazy" decoding="async" class="h-24 w-24 rounded-lg object-cover" />
            <button
              type="button"
              class="absolute right-1 top-1 rounded-full bg-black/50 px-1.5 text-white opacity-0 transition group-hover/att:opacity-100"
              aria-label="Remove image"
              @click="notes.deleteAttachment(liveNote.id, att.id)"
            >
              ×
            </button>
          </div>
        </div>
        <p v-if="uploadError" class="text-xs text-red-600 dark:text-red-400">{{ uploadError }}</p>

        <input
          v-model="title"
          type="text"
          placeholder="Title (optional)"
          class="w-full bg-transparent text-base font-semibold outline-none placeholder:text-neutral-400"
          @keydown.enter="onTitleEnter"
        />

        <div v-if="!showChecklist" class="relative">
          <textarea
            ref="bodyInput"
            v-model="body"
            :rows="inline ? undefined : 8"
            :placeholder="bodyPlaceholder"
            :class="
              inline
                ? 'max-h-64 min-h-[4.5rem] w-full resize-none overflow-y-auto bg-transparent text-sm leading-relaxed outline-none placeholder:text-neutral-400'
                : 'w-full resize-none bg-transparent text-sm leading-relaxed outline-none placeholder:text-neutral-400'
            "
            @input="onBodyInput"
            @keydown="onBodyKeydown"
          />
          <ul
            v-if="linkMenu && linkMatches.length"
            class="absolute left-0 top-full z-10 mt-1 max-h-48 w-64 overflow-y-auto rounded-lg border border-neutral-200 bg-white p-1 shadow-lg dark:border-neutral-700 dark:bg-neutral-800"
          >
            <li v-for="(m, i) in linkMatches" :key="m.id">
              <button
                type="button"
                class="flex w-full items-center rounded-md px-2 py-1.5 text-left text-sm"
                :class="
                  i === linkSelected
                    ? 'bg-brand/15 text-brand-700 dark:text-brand'
                    : 'hover:bg-neutral-100 dark:hover:bg-neutral-700'
                "
                @mousemove="linkSelected = i"
                @mousedown.prevent="insertLink(m.title)"
              >
                <span class="truncate">{{ m.title }}</span>
              </button>
            </li>
          </ul>
        </div>
        <NoteChecklist v-else class="py-1" :note-id="liveNote.id" :items="liveNote.items" editable />

        <div v-if="labelList.length" class="flex flex-wrap gap-1.5 pt-1">
          <span
            v-for="lb in labelList"
            :key="lb.id"
            class="inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs"
            :class="labelChip(lb.color)"
          >
            {{ lb.via_tag ? "#" + lb.name : lb.name }}
            <button
              v-if="!lb.via_tag"
              type="button"
              class="text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-100"
              :aria-label="`Remove ${lb.name}`"
              @click="removeLabel(lb.id)"
            >
              ×
            </button>
          </span>
        </div>

        <div v-if="richEnabled" class="flex items-center gap-2 pt-1">
          <Icon name="bell" class="text-neutral-400" />
          <input
            type="datetime-local"
            :value="reminderLocal"
            class="rounded-md border border-neutral-300 bg-white px-2 py-1 text-xs text-neutral-700 outline-none [color-scheme:light] focus-visible:ring-2 focus-visible:ring-brand dark:border-neutral-700 dark:bg-neutral-800 dark:text-neutral-200 dark:[color-scheme:dark]"
            @change="onReminderChange"
          />
          <button
            v-if="liveNote.remind_at"
            type="button"
            class="text-xs text-neutral-400 hover:text-neutral-700 dark:hover:text-neutral-200"
            @click="notes.setReminder(liveNote.id, null)"
          >
            Clear
          </button>
        </div>

        <div
          v-if="!inline && !isCreate && (outgoingLinks.length || backlinks.length)"
          class="flex flex-col gap-2 border-t border-neutral-100 pt-2 dark:border-neutral-800"
        >
          <div v-if="outgoingLinks.length">
            <p class="mb-1 text-xs font-semibold uppercase tracking-wide text-neutral-400">Links</p>
            <div class="flex flex-wrap gap-1.5">
              <button
                v-for="link in outgoingLinks"
                :key="link.title"
                type="button"
                class="inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs"
                :class="
                  link.id
                    ? 'bg-brand/15 text-brand-700 dark:text-brand'
                    : 'bg-black/5 text-neutral-500 dark:bg-white/10 dark:text-neutral-400'
                "
                :title="link.id ? `Open ${link.title}` : `Create ${link.title}`"
                @click="openLink(link)"
              >
                {{ link.title }}<span v-if="!link.id" class="opacity-60">＋</span>
              </button>
            </div>
          </div>
          <div v-if="backlinks.length">
            <p class="mb-1 text-xs font-semibold uppercase tracking-wide text-neutral-400">Linked from</p>
            <div class="flex flex-wrap gap-1.5">
              <button
                v-for="b in backlinks"
                :key="b.id"
                type="button"
                class="inline-flex items-center rounded-full bg-black/5 px-2 py-0.5 text-xs text-neutral-600 hover:bg-black/10 dark:bg-white/10 dark:text-neutral-300"
                @click="emit('navigate', b.id)"
              >
                {{ b.title }}
              </button>
            </div>
          </div>
        </div>

        <div
          v-if="!inline && !isCreate && showHistory"
          class="flex flex-col gap-1 border-t border-neutral-100 pt-2 dark:border-neutral-800"
        >
          <p class="text-xs font-semibold uppercase tracking-wide text-neutral-400">History</p>
          <p v-if="!revisions.length" class="text-xs text-neutral-400">
            No earlier versions yet — your edits will show up here.
          </p>
          <ul v-else class="flex max-h-40 flex-col gap-0.5 overflow-y-auto">
            <li v-for="rev in revisions" :key="rev.id" class="flex items-center gap-2 rounded-md px-1 py-1 text-xs">
              <span class="shrink-0 tabular-nums text-neutral-400">{{ revLabel(rev.created_at) }}</span>
              <span class="min-w-0 flex-1 truncate text-neutral-600 dark:text-neutral-300">{{ revPreview(rev) }}</span>
              <button
                type="button"
                class="shrink-0 rounded px-1.5 py-0.5 font-medium text-brand-700 hover:bg-brand/10 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand dark:text-brand"
                @click="restoreRevisionAt(rev.id)"
              >
                Restore
              </button>
            </li>
          </ul>
        </div>
      </div>

      <div class="flex items-center justify-between gap-2 border-t border-neutral-100 px-3 py-2 dark:border-neutral-800">
        <ColorPicker v-model="color" />
        <div class="flex items-center gap-0.5">
          <button
            v-if="richEnabled && !liveNote.trashed"
            type="button"
            class="icon-btn"
            title="Add image"
            aria-label="Add image"
            @click="pickImage"
          >
            <Icon name="image" />
          </button>
          <input
            ref="fileInput"
            type="file"
            accept="image/png,image/jpeg,image/gif,image/webp"
            class="hidden"
            @change="onFileChange"
          />
          <button
            v-if="!liveNote.trashed"
            type="button"
            class="icon-btn"
            :class="isListMode ? 'text-brand-700 dark:text-brand' : ''"
            :title="isListMode ? 'Switch to a note' : 'Make a checklist'"
            :aria-pressed="isListMode"
            @click="toggleKind"
          >
            <Icon name="checkbox" />
          </button>
          <LabelPicker
            v-if="richEnabled && !liveNote.trashed"
            :model-value="labelList"
            @update:model-value="onLabelsChange"
          />
          <button
            v-if="!inline && !isCreate"
            type="button"
            class="icon-btn"
            :class="showHistory ? 'text-brand-700 dark:text-brand' : ''"
            title="Version history"
            aria-label="Version history"
            :aria-pressed="showHistory"
            @click="toggleHistory"
          >
            <Icon name="history" />
          </button>
          <template v-if="!inline && !isCreate && !liveNote.trashed">
            <button
              type="button"
              class="icon-btn"
              :class="liveNote.pinned ? 'text-brand-700 dark:text-brand' : ''"
              :title="liveNote.pinned ? 'Unpin' : 'Pin'"
              @click="act(() => notes.setPinned(liveNote.id, !liveNote.pinned))"
            >
              <Icon name="pin" />
            </button>
            <button
              type="button"
              class="icon-btn"
              :title="liveNote.archived ? 'Unarchive' : 'Archive'"
              @click="act(() => notes.setArchived(liveNote.id, !liveNote.archived))"
            >
              <Icon name="archive" />
            </button>
            <button
              type="button"
              class="icon-btn"
              title="Move to trash"
              @click="act(() => notes.trash(liveNote.id))"
            >
              <Icon name="trash" />
            </button>
          </template>
          <template v-else-if="!inline && !isCreate && liveNote.trashed">
            <button type="button" class="icon-btn" title="Restore" @click="act(() => notes.restore(liveNote.id))">
              <Icon name="restore" />
            </button>
            <button
              type="button"
              class="icon-btn"
              title="Delete forever"
              @click="act(() => notes.deleteForever(liveNote.id))"
            >
              <Icon name="trash" />
            </button>
          </template>
          <button
            type="button"
            class="rounded-md px-3 py-1.5 text-sm font-semibold text-neutral-700 hover:bg-neutral-100 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand disabled:opacity-60 dark:text-neutral-200 dark:hover:bg-neutral-800"
            :disabled="saving"
            @click="inline ? commitInline() : close()"
          >
            {{ inline ? "Done" : "Close" }}
          </button>
        </div>
      </div>
    </div>
  </div>
</template>
