import { EditorView, basicSetup } from 'codemirror'
import { Shrimp } from '#/index'

import { shrimpTheme } from './theme'
import { createShrimpDiagnostics } from './diagnostics'
import { shrimpHighlighter } from './highlighter'
import { createShrimpCompletions } from './completions'
import { shrimpKeymap } from './keymap'
import { getContent, persistence } from './persistence'

type EditorProps = {
  initialCode?: string
  onChange?: (code: string) => void
  extensions?: import('@codemirror/state').Extension[]
  shrimp?: Shrimp
}

export const Editor = ({
  initialCode = '',
  onChange,
  extensions: customExtensions = [],
  shrimp = new Shrimp(),
}: EditorProps) => {
  return (
    <div
      ref={(el: Element) => {
        if (!el?.querySelector('.cm-editor'))
          createEditorView(el, getContent() ?? initialCode, onChange, customExtensions, shrimp)
      }}
    />
  )
}

const createEditorView = (
  el: Element,
  initialCode: string,
  onChange: ((code: string) => void) | undefined,
  customExtensions: import('@codemirror/state').Extension[],
  shrimp: Shrimp
) => {
  const extensions = [
    basicSetup,
    shrimpTheme,
    createShrimpDiagnostics(shrimp),
    createShrimpCompletions(shrimp),
    shrimpHighlighter,
    shrimpKeymap,
    persistence,
    ...customExtensions,
  ]

  if (onChange) {
    extensions.push(
      EditorView.updateListener.of((update) => {
        if (update.docChanged) {
          onChange(update.state.doc.toString())
        }
      })
    )
  }

  new EditorView({
    parent: el,
    doc: initialCode,
    extensions,
  })

  // Trigger onChange with initial content
  onChange?.(initialCode)
}
