import {css, html, LitElement} from 'lit'
import {customElement, property} from 'lit/decorators.js'
import {Ex} from '../../util'
import uuid = Ex.uuid
import {EditorView, basicSetup} from 'codemirror'
import {keymap, placeholder} from '@codemirror/view'
import {EditorState} from '@codemirror/state'
import {indentWithTab} from '@codemirror/commands'
import * as langHtml from '@codemirror/lang-html'

export class CodeChangeEvent extends Event {
    constructor(public readonly content: string, baseEventContent?: EventInit) {
        super('code-change', baseEventContent)
    }
}

@customElement('cobalt-code')
export class Code extends LitElement {
    static override styles = css`
        .editor-container {
            width: calc(100% - 10px);
            background: #ffffff;
            padding: 5px;
            border: 1px solid #999;
            border-radius: 7px;
        }
    `

    @property()
    initialValue?: string

    @property()
    syntax?: string

    @property()
    placeholder?: string

    @property()
    required?: boolean

    @property()
    disabled?: boolean

    @property()
    label?: string

    @property()
    helpText?: string

    private editorId: string = 'cobalt-code-' + uuid()
    private value: string = ''
    private editor?: EditorView

    override connectedCallback() {
        super.connectedCallback()

        requestAnimationFrame(() => {
            const el = this.getEditorContainer()
            if ( !el ) {
                console.warn('Could not initialize cobalt-code: could not get editor container element')
                return
            }

            this.initializeEditor(el)
        })
    }

    override render() {
        const helpText = this.helpText ? html`<small>${this.helpText}</small>` : html``

        return html`
            <slot name="label">${this.label}${this.required ? '*' : ''}</slot>
            <div
                class="editor-container"
                id=${this.editorId}
            ></div>
            ${helpText}
        `
    }

    private initializeEditor(parent: HTMLElement) {
        this.value = this.initialValue || ''

        const extensions = [
            basicSetup,
            keymap.of([indentWithTab]),
            EditorView.updateListener.of((e) => {
                if ( e.docChanged ) {
                    this.dispatchEvent(new CodeChangeEvent(this.editor!.state.doc.toString()))
                }
            })
        ]

        const lang = this.getLangPlugin()
        if ( lang ) {
            extensions.push(lang)
        }

        if ( this.placeholder ) {
            extensions.push(placeholder(this.placeholder))
        }

        if ( this.disabled ) {
            extensions.push(EditorState.readOnly.of(true))
        }

        this.editor = new EditorView({
            extensions,
            parent,
            doc: this.value,
        })
    }

    private getLangPlugin() {
        if ( this.syntax === 'html' ) {
            return langHtml.html()
        }

        return undefined
    }

    private getEditorContainer(): HTMLElement | null {
        return (this.shadowRoot || this).querySelector(`#${this.editorId}`)
    }
}
