<script>
  import { onMount, onDestroy } from 'svelte'
  import { EditorView, keymap, lineNumbers, highlightActiveLine, placeholder as cmPlaceholder } from '@codemirror/view'
  import { EditorState } from '@codemirror/state'
  import { defaultKeymap, history, historyKeymap, indentWithTab } from '@codemirror/commands'
  import { StreamLanguage, syntaxHighlighting, HighlightStyle } from '@codemirror/language'
  import { tags } from '@lezer/highlight'
  import { indentationMarkers } from '@replit/codemirror-indentation-markers'

  export let value = ''
  export let rows = 10
  export let placeholder = ''
  export let onchange = null   // callback(newValue)
  // Extra CodeMirror keymap entries: [{ key: 'Escape', run: (view) => bool }, ...]
  export let extraKeys = []

  let container
  let view

  // Minimal Pug StreamParser
  const pugLanguage = StreamLanguage.define({
    token(stream) {
      if (stream.match(/\/\/.*/)) return 'comment'
      if (stream.match(/\/\*.*?\*\//)) return 'comment'
      if (stream.match(/\+[a-zA-Z_][\w-]*/)) return 'keyword'
      if (stream.match(/mixin\s+/)) return 'keyword'
      if (stream.sol() && stream.match(/\s*[-=]/)) { stream.skipToEnd(); return 'meta' }
      if (stream.match(/"(?:[^"\\]|\\.)*"/)) return 'string'
      if (stream.match(/'(?:[^'\\]|\\.)*'/)) return 'string'
      if (stream.match(/#\{[^}]*\}/)) return 'string'
      if (stream.match(/[.#][a-zA-Z_][\w-]*/)) return 'typeName'
      if (stream.sol() && stream.match(/\s*[a-zA-Z][a-zA-Z0-9]*/)) return 'tagName'
      if (stream.match(/[a-zA-Z_:][\w-]*(?=\s*=)/)) return 'attributeName'
      stream.next()
      return null
    }
  })

  const highlightStyle = HighlightStyle.define([
    { tag: tags.keyword,       color: '#569cd6' },  // синий — +mixin
    { tag: tags.tagName,       color: '#4ec9b0' },  // бирюзовый — теги
    { tag: tags.typeName,      color: '#4ec9b0' },  // бирюзовый — .class #id
    { tag: tags.attributeName, color: '#9cdcfe' },  // голубой — атрибуты
    { tag: tags.string,        color: '#ce9178' },  // оранжевый — строки (читаемый)
    { tag: tags.comment,       color: '#6a9955', fontStyle: 'italic' },
    { tag: tags.meta,          color: '#c586c0' },  // фиолетовый — - var / = expr
  ])

  const theme = EditorView.theme({
    '&': {
      fontSize: '12px',
      fontFamily: 'ui-monospace, "SF Mono", Menlo, Monaco, "Cascadia Code", monospace',
      background: '#1e1e1e',
      color: '#d4d4d4',
      borderRadius: '4px',
      border: '1px solid #444',
      minHeight: `${rows * 1.5}em`,
    },
    '&.cm-focused': { outline: '1px solid #4a9eff', border: '1px solid #4a9eff' },
    '.cm-content': { padding: '6px 4px', caretColor: '#d4d4d4' },
    '.cm-line': { lineHeight: '1.5' },
    '.cm-gutters': {
      background: '#1e1e1e', color: '#888',
      border: 'none', borderRight: '1px solid #444', minWidth: '32px'
    },
    '.cm-lineNumbers .cm-gutterElement': { padding: '0 6px 0 4px', fontSize: '11px' },
    '.cm-activeLine': { background: 'rgba(255,255,255,0.07)' },
    '.cm-activeLineGutter': { background: 'rgba(255,255,255,0.07)' },
    '.cm-placeholder': { color: '#888' },
    '.cm-indent-markers': { '--indent-marker-bg-color': 'rgba(255,255,255,0.08)', '--indent-marker-active-bg-color': 'rgba(255,255,255,0.15)' },
  }, { dark: true })


  onMount(() => {
    view = new EditorView({
      state: EditorState.create({
        doc: value,
        extensions: [
          history(),
          lineNumbers(),
          highlightActiveLine(),
          keymap.of([...extraKeys, indentWithTab, ...defaultKeymap, ...historyKeymap]),
          pugLanguage,
          syntaxHighlighting(highlightStyle),
          theme,
          indentationMarkers({ highlightActiveBlock: true }),
          placeholder ? cmPlaceholder(placeholder) : [],
          EditorView.updateListener.of(update => {
            if (update.docChanged) {
              const newVal = view.state.doc.toString()
              if (newVal !== value) {
                value = newVal
                onchange?.(newVal)
              }
            }
          }),
        ]
      }),
      parent: container
    })
  })

  onDestroy(() => view?.destroy())

  // Sync external value changes → editor
  $: if (view) {
    const current = view.state.doc.toString()
    if (value !== current) {
      view.dispatch({ changes: { from: 0, to: current.length, insert: value } })
    }
  }
</script>

<div bind:this={container} class="code-editor-wrap"></div>

<style>
  .code-editor-wrap { width: 100%; }
  .code-editor-wrap :global(.cm-editor) { width: 100%; }
</style>
