<script setup lang="ts">
import { onBeforeUnmount, watch } from "vue";
import { useEditor, EditorContent } from "@tiptap/vue-3";
import StarterKit from "@tiptap/starter-kit";
import {
  Bold,
  Braces,
  Code,
  Heading1,
  Heading2,
  Heading3,
  Italic,
  List,
  ListOrdered,
  Minus,
  Pilcrow,
  Redo2,
  Strikethrough,
  TextQuote,
  Undo2,
} from "lucide-vue-next";
import { cn } from "@/lib/utils";
import type { FieldDefinition } from "./types";

const props = defineProps<{
  field: FieldDefinition;
  modelValue?: unknown;
  error?: string;
}>();

const emit = defineEmits<{
  "update:modelValue": [value: string];
}>();

const editor = useEditor({
  content: (props.modelValue as string) ?? "",
  extensions: [StarterKit],
  editorProps: {
    attributes: {
      class: "tiptap",
    },
  },
  onUpdate: ({ editor: e }) => {
    emit("update:modelValue", e.getHTML());
  },
});

watch(
  () => props.modelValue,
  (val) => {
    if (editor.value && val !== editor.value.getHTML()) {
      editor.value.commands.setContent((val as string) ?? "", false);
    }
  },
);

onBeforeUnmount(() => editor.value?.destroy());

function btn(active: boolean): string {
  return cn(
    "inline-flex h-7 w-7 items-center justify-center rounded text-muted-foreground transition-colors",
    "hover:bg-accent hover:text-accent-foreground",
    "disabled:pointer-events-none disabled:opacity-50",
    active && "bg-muted text-foreground",
  );
}

function textBtn(active: boolean): string {
  return cn(
    "inline-flex h-7 min-w-[1.75rem] items-center justify-center rounded px-1 text-xs font-medium text-muted-foreground transition-colors",
    "hover:bg-accent hover:text-accent-foreground",
    "disabled:pointer-events-none disabled:opacity-50",
    active && "bg-muted text-foreground",
  );
}
</script>

<template>
  <div
    :class="
      cn(
        'border-input bg-background ring-offset-background focus-within:ring-ring rounded-md border shadow-sm transition-colors focus-within:ring-1',
        field.readonly && 'cursor-not-allowed opacity-50',
        props.error && 'border-destructive',
      )
    "
  >
    <!-- Toolbar -->
    <div
      class="flex flex-wrap items-center gap-0.5 border-b border-input bg-muted/40 px-2 py-1.5"
    >
      <!-- History -->
      <button
        type="button"
        :class="btn(false)"
        :disabled="field.readonly || !editor?.can().undo()"
        @click="editor?.chain().focus().undo().run()"
      >
        <Undo2 class="size-3.5" />
      </button>
      <button
        type="button"
        :class="btn(false)"
        :disabled="field.readonly || !editor?.can().redo()"
        @click="editor?.chain().focus().redo().run()"
      >
        <Redo2 class="size-3.5" />
      </button>

      <span class="mx-1 h-4 w-px bg-border" />

      <!-- Inline formatting -->
      <button
        type="button"
        :class="btn(editor?.isActive('bold') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleBold().run()"
      >
        <Bold class="size-3.5" />
      </button>
      <button
        type="button"
        :class="btn(editor?.isActive('italic') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleItalic().run()"
      >
        <Italic class="size-3.5" />
      </button>
      <button
        type="button"
        :class="btn(editor?.isActive('strike') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleStrike().run()"
      >
        <Strikethrough class="size-3.5" />
      </button>
      <button
        type="button"
        :class="btn(editor?.isActive('code') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleCode().run()"
      >
        <Code class="size-3.5" />
      </button>

      <span class="mx-1 h-4 w-px bg-border" />

      <!-- Headings + paragraph -->
      <button
        type="button"
        :class="textBtn(editor?.isActive('heading', { level: 1 }) ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleHeading({ level: 1 }).run()"
      >
        <Heading1 class="size-3.5" />
      </button>
      <button
        type="button"
        :class="textBtn(editor?.isActive('heading', { level: 2 }) ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleHeading({ level: 2 }).run()"
      >
        <Heading2 class="size-3.5" />
      </button>
      <button
        type="button"
        :class="textBtn(editor?.isActive('heading', { level: 3 }) ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleHeading({ level: 3 }).run()"
      >
        <Heading3 class="size-3.5" />
      </button>
      <button
        type="button"
        :class="textBtn(editor?.isActive('paragraph') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().setParagraph().run()"
      >
        <Pilcrow class="size-3.5" />
      </button>

      <span class="mx-1 h-4 w-px bg-border" />

      <!-- Lists -->
      <button
        type="button"
        :class="btn(editor?.isActive('bulletList') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleBulletList().run()"
      >
        <List class="size-3.5" />
      </button>
      <button
        type="button"
        :class="btn(editor?.isActive('orderedList') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleOrderedList().run()"
      >
        <ListOrdered class="size-3.5" />
      </button>

      <span class="mx-1 h-4 w-px bg-border" />

      <!-- Block elements -->
      <button
        type="button"
        :class="btn(editor?.isActive('blockquote') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleBlockquote().run()"
      >
        <TextQuote class="size-3.5" />
      </button>
      <button
        type="button"
        :class="btn(false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().setHorizontalRule().run()"
      >
        <Minus class="size-3.5" />
      </button>
      <button
        type="button"
        :class="btn(editor?.isActive('codeBlock') ?? false)"
        :disabled="field.readonly"
        @click="editor?.chain().focus().toggleCodeBlock().run()"
      >
        <Braces class="size-3.5" />
      </button>
    </div>

    <EditorContent :editor="editor" />
  </div>
</template>
