/* global Node, HTMLElement */

const TOOLBAR_ACTIONS = [
    { command: 'bold', icon: '<b>B</b>', title: 'Gras' },
    { command: 'italic', icon: '<i>I</i>', title: 'Italique' },
    { command: 'underline', icon: '<u>S</u>', title: 'Souligne' },
    { separator: true },
    { command: 'formatBlock', value: 'P', icon: 'P', title: 'Paragraphe' },
    { separator: true },
    { command: 'insertUnorderedList', icon: '&bull;', title: 'Liste a puces' },
    { separator: true },
    { command: 'removeFormat', icon: '&#10060;', title: 'Supprimer le formatage' },
]

const ALLOWED_TAGS = new Set([
    'p', 'br', 'b', 'strong', 'i', 'em', 'u',
    'ul', 'li',
])

const BLOCKED_TAGS = new Set([
    'script', 'style', 'iframe', 'object', 'embed', 'form', 'input', 'select', 'textarea',
    'link', 'meta', 'noscript', 'template', 'svg', 'math',
])

export function sanitizeHtml(html) {
    const container = document.createElement('div')
    container.innerHTML = html
    const fragment = sanitizeNode(container)
    const wrapper = document.createElement('div')
    wrapper.appendChild(fragment)

    return wrapper.innerHTML.trim()
}

function sanitizeNode(node) {
    const fragment = document.createDocumentFragment()

    for (const child of Array.from(node.childNodes)) {
        if (child.nodeType === Node.TEXT_NODE) {
            fragment.appendChild(document.createTextNode(child.textContent))
        } else if (child.nodeType === Node.ELEMENT_NODE) {
            const tagName = child.tagName.toLowerCase()
            if (BLOCKED_TAGS.has(tagName)) {
                continue
            }
            if (ALLOWED_TAGS.has(tagName)) {
                // createElement produces a bare element — no attributes from source are copied,
                // which strips onclick, style, class, id, onerror, etc. by design.
                const el = document.createElement(tagName)
                el.appendChild(sanitizeNode(child))
                fragment.appendChild(el)
            } else {
                fragment.appendChild(sanitizeNode(child))
            }
        }
    }

    return fragment
}

export class ETicketEditor extends HTMLElement {
    connectedCallback() {
        const textarea = this.querySelector('textarea')
        if (!textarea) {
            return
        }

        this._textarea = textarea
        textarea.style.display = 'none'

        this._buildToolbar()
        this._buildContentArea()

        if (textarea.value) {
            this._content.innerHTML = textarea.value
        }

        this._content.addEventListener('input', () => this._sync())
        this._content.addEventListener('keydown', (e) => this._onKeydown(e))
    }

    _buildToolbar() {
        const toolbar = document.createElement('div')
        toolbar.classList.add('ete-toolbar')

        for (const action of TOOLBAR_ACTIONS) {
            if (action.separator) {
                const sep = document.createElement('span')
                sep.classList.add('ete-separator')
                toolbar.appendChild(sep)
                continue
            }

            const btn = document.createElement('button')
            btn.type = 'button'
            btn.classList.add('ete-btn')
            btn.innerHTML = action.icon
            btn.title = action.title
            btn.setAttribute('aria-label', action.title)
            btn.tabIndex = 0
            btn.addEventListener('mousedown', (e) => {
                e.preventDefault()
                this._exec(action)
            })
            toolbar.appendChild(btn)
        }

        this.insertBefore(toolbar, this._textarea)
    }

    _buildContentArea() {
        const content = document.createElement('div')
        content.classList.add('ete-content')
        content.setAttribute('contenteditable', 'true')
        content.setAttribute('role', 'textbox')
        content.setAttribute('aria-multiline', 'true')

        const placeholder = this._textarea.getAttribute('placeholder')
        if (placeholder) {
            content.dataset.placeholder = placeholder
        }

        this.insertBefore(content, this._textarea)
        this._content = content
    }

    _exec(action) {
        if (action.value) {
            document.execCommand(action.command, false, action.value)
        } else {
            document.execCommand(action.command, false, null)
        }

        this._sync()
        this._content.focus()
    }

    _onKeydown(e) {
        if (e.key === 'Tab') {
            e.preventDefault()
        }
    }

    _sync() {
        const html = sanitizeHtml(this._content.innerHTML)
        this._textarea.value = html
    }

    getHtml() {
        return this._textarea.value
    }
}

export function registerEditor() {
    if (!globalThis.customElements.get('e-ticket-editor')) {
        globalThis.customElements.define('e-ticket-editor', ETicketEditor)
    }
}
