<template>
    <main v-if="editor" class="editor layout-block">
        <bubble-menu :editor="editor">
            <div class="bubble-menu">
                <button
                    @click="editor.chain().focus().toggleBold().run()"
                    :class="{ active: editor.isActive('bold') }"
                >
                    Bold
                </button>
                <button
                    @click="editor.chain().focus().toggleItalic().run()"
                    :class="{ active: editor.isActive('italic') }"
                >
                    Italic
                </button>
                <button
                    @click="editor.chain().focus().toggleHighlight().run()"
                    :class="{ active: editor.isActive('highlight') }"
                >
                    Highlight
                </button>
            </div>
        </bubble-menu>

        <editor-content :editor="editor" class="editor-wrap" />
    </main>
</template>

<script setup>
import { onBeforeUnmount, onMounted, shallowRef } from 'vue'
import { Editor, EditorContent } from '@tiptap/vue-3'
import Document from '@tiptap/extension-document'
import { Placeholder } from '@tiptap/extensions'
import StarterKit from '@tiptap/starter-kit'
import { TaskList, TaskItem } from '@tiptap/extension-list'
import { Highlight } from '@tiptap/extension-highlight'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import { BubbleMenu } from '@tiptap/vue-3/menus'
import { all, createLowlight } from 'lowlight'
import useNotes from '@/composables/useNotes'
import { useRoute } from 'vue-router'
import _debounce from 'lodash/debounce'

const route = useRoute()
const id = route.params.id

const { loadNote, updateNoteContent, updateNoteMetadata } = useNotes()

const CustomDocument = Document.extend({
    content: 'heading block*',
})

const editor = shallowRef()

const updateNote = _debounce(async ({ editor }) => {
    const json = editor.getJSON()

    await updateNoteContent(id, json)

    updateTitle(editor)
}, 300)

// Track title updates for file
let lastTitle
const updateTitle = _debounce(async (editor) => {
    const doc = editor.state.doc

    const firstNode = doc.firstChild
    if (!firstNode || firstNode.type.name !== 'heading') return

    const newTitle = firstNode.textContent.trim() || 'Untitled'

    if (newTitle === lastTitle) return
    lastTitle = newTitle

    await updateNoteMetadata(id, { title: newTitle })
}, 300)

onMounted(async () => {
    const note = await loadNote(id)
    lastTitle = note.title

    // Lowlight setup
    const lowlight = createLowlight(all)

    editor.value = new Editor({
        extensions: [
            CustomDocument,
            StarterKit.configure({
                document: false,
                heading: { levels: [1] },
                trailingNode: {
                    node: 'paragraph',
                },
            }),
            Placeholder.configure({
                placeholder: ({ node }) => {
                    if (node.type.name === 'heading') {
                        return 'Title'
                    }
                },
            }),
            TaskList,
            TaskItem,
            Highlight,
            CodeBlockLowlight.configure({
                lowlight,
                enableTabIndentation: true,
            }),
        ],
        content: note.content || [],
        onUpdate: updateNote,
    })
})
onBeforeUnmount(() => {
    editor.value?.destroy?.()
})
</script>

<style lang="scss">
main.editor {
    padding-top: size-vw(8px);
    padding-bottom: size-vw(20px);

    h1 {
        font-weight: 700 !important;
        @include p;

        &:first-child {
            @include drop-cap;
        }
    }
    h1.is-editor-empty:first-child::before {
        color: var(--grey-100);
        content: attr(data-placeholder);
        pointer-events: none;
        @include drop-cap;
    }
    p strong {
        font-weight: 700;
    }
    p em {
        font-style: italic;
    }
    hr::before {
        content: '----------------------------------------';
        @include p;
    }
    ul {
        list-style-type: disc;

        li {
            display: list-item;
            margin-left: 1em;
        }
    }
    ol {
        list-style-type: decimal;

        li {
            display: list-item;
            margin-left: 1.5em;

            &::marker {
                @include p;
            }
        }
    }
    a {
        color: var(--theme-link);
        cursor: pointer;
    }
    code {
        border: 1px solid var(--grey-100);
        color: var(--theme-accent);
        padding: 0.2em;
        border-radius: 0.2em;
    }
    pre code {
        display: block;
        color: inherit;
        padding: 1em;

        /* Code styling */
        .hljs-comment,
        .hljs-quote {
            color: #616161;
        }

        .hljs-variable,
        .hljs-template-variable,
        .hljs-attribute,
        .hljs-tag,
        .hljs-name,
        .hljs-regexp,
        .hljs-link,
        .hljs-name,
        .hljs-selector-id,
        .hljs-selector-class {
            color: #f98181;
        }

        .hljs-number,
        .hljs-meta,
        .hljs-built_in,
        .hljs-builtin-name,
        .hljs-literal,
        .hljs-type,
        .hljs-params {
            color: #fbbc88;
        }

        .hljs-string,
        .hljs-symbol,
        .hljs-bullet {
            color: #b9f18d;
        }

        .hljs-title,
        .hljs-section {
            color: #faf594;
        }

        .hljs-keyword,
        .hljs-selector-tag {
            color: #70cff8;
        }

        .hljs-emphasis {
            font-style: italic;
        }

        .hljs-strong {
            font-weight: 700;
        }
    }
    blockquote {
        border-left: 4px solid var(--grey-100);
        padding-left: 0.5em;
    }
    s {
        position: relative;

        &::after {
            content: ' ';
            display: block;
            position: absolute;
            top: 50%;
            left: 0;
            right: 0;
            height: size-vw(1px);
            background: currentColor;
        }
    }
    mark {
        background: var(--theme-accent);
        color: var(--theme-bg);
        padding: 0.2em;
        border-radius: 0.2em;
    }

    .editor-wrap {
        > div {
            display: flex;
            flex-direction: column;
            gap: size-vw(20px);

            &:focus {
                outline: none;
            }
        }
    }

    .bubble-menu {
        display: flex;
        gap: size-vw(5px);
        border: 1px solid var(--grey-100);
        color: var(--grey-100);
        border-radius: 0.2em;
        background: var(--theme-bg);

        button {
            cursor: pointer;
            padding: 0.2em;
            border-radius: 0.2em;

            &:hover {
                background: var(--grey-100);
                color: var(--theme-bg);
            }
            &.active {
                background: var(--theme-fg);
                color: var(--theme-bg);
            }
        }
    }
}
</style>
