/**
 * Shared code editor with Nunjucks support by default.
 * Uses react-simple-code-editor + Prism + prism-react-renderer; highlights base language + {{ }}, {% %}, {# #}.
 * Wherever this editor is used, it provides the same features (syntax highlighting + Nunjucks).
 * The parent (node) provides the expected base language for highlighting.
 */
import React, { useCallback, useLayoutEffect, useRef } from 'react'
import Editor from 'react-simple-code-editor'
import { highlight, type HighlightLanguage } from '@/lib/syntaxHighlight'

export type CodeEditorProps = {
  value: string
  onValueChange: (value: string) => void
  /** Base language for syntax highlighting (Nunjucks is always applied on top). */
  language: HighlightLanguage
  /** Stable id for the underlying textarea (for insert-at-cursor). */
  textareaId?: string
  readOnly?: boolean
  placeholder?: string
  padding?: number
  tabSize?: number
  insertSpaces?: boolean
  ignoreTabKey?: boolean
  style?: React.CSSProperties
  className?: string
  textareaClassName?: string
  preClassName?: string
  /** Minimum height so the container doesn't collapse before resize. */
  minHeight?: number
}

const defaultStyle: React.CSSProperties = {
  fontFamily: 'ui-monospace, monospace',
  fontSize: 12,
  lineHeight: 1.5,
  overflow: 'auto',
}

export function CodeEditor({
  value,
  onValueChange,
  language,
  textareaId,
  readOnly = false,
  padding = 8,
  tabSize = 2,
  insertSpaces = true,
  ignoreTabKey = false,
  style,
  className,
  textareaClassName,
  preClassName,
  minHeight = 120,
}: CodeEditorProps) {
  const containerRef = useRef<HTMLDivElement | null>(null)
  const selectionRestoreRef = useRef<{ start: number; end: number } | null>(null)

  const highlightCode = useCallback(
    (code: string) => highlight(code, language),
    [language]
  )

  const handleValueChange = useCallback(
    (newValue: string) => {
      if (readOnly) {
        onValueChange(newValue)
        return
      }
      const ta =
        (textareaId ? document.getElementById(textareaId) : containerRef.current?.querySelector('textarea')) as HTMLTextAreaElement | null
      const start = ta?.selectionStart ?? newValue.length
      const end = ta?.selectionEnd ?? newValue.length
      selectionRestoreRef.current = { start, end }
      onValueChange(newValue)
    },
    [onValueChange, readOnly, textareaId]
  )

  useLayoutEffect(() => {
    const pending = selectionRestoreRef.current
    if (pending == null) return
    selectionRestoreRef.current = null
    const el = (textareaId
      ? document.getElementById(textareaId)
      : containerRef.current?.querySelector('textarea')) as HTMLTextAreaElement | null
    if (el) {
      const { start, end } = pending
      const safeEnd = Math.min(end, el.value.length)
      const safeStart = Math.min(start, safeEnd)
      el.setSelectionRange(safeStart, safeEnd)
    }
  }, [value, textareaId])

  return (
    <div ref={containerRef} className="code-editor min-h-0 h-full overflow-auto" style={{ minHeight: 0 }}>
      <Editor
        value={value}
        onValueChange={handleValueChange}
        highlight={highlightCode}
        tabSize={tabSize}
        insertSpaces={insertSpaces}
        ignoreTabKey={ignoreTabKey}
        padding={padding}
        readOnly={readOnly}
        textareaId={textareaId}
        style={{ ...defaultStyle, minHeight, ...style }}
        className={className}
        textareaClassName={textareaClassName}
        preClassName={preClassName}
      />
    </div>
  )
}
