'use client'

import { useMemo, useCallback, useEffect, useState } from 'react'
import dynamic from 'next/dynamic'
import type { Extension } from '@codemirror/state'
import { EditorView, keymap } from '@codemirror/view'
import { indentWithTab } from '@codemirror/commands'
import { StreamLanguage } from '@codemirror/language'
import { cn } from '@/lib/utils'
import { useTheme } from '@/components/providers/theme-provider'
import { forgeDark, forgeLight } from '@/lib/codemirror-theme'
import { CodeEditor } from './code-editor'
import type { EditorLanguage } from '@/lib/file-types'

/** Large file threshold — fallback to plain textarea above this size */
const LARGE_FILE_THRESHOLD = 500_000 // ~500KB

// CodeMirror requires browser environment — disable SSR
const ReactCodeMirror = dynamic(
  () => import('@uiw/react-codemirror').then((mod) => mod.default),
  { ssr: false }
)

interface CodeMirrorEditorProps {
  value: string
  onChange: (value: string) => void
  language?: EditorLanguage
  readOnly?: boolean
  wordWrap?: boolean
  className?: string
}

/**
 * Map of language identifiers to dynamic language-pack imports.
 * Only loads the grammar when the file is actually opened.
 */
const LANG_IMPORTS: Record<string, () => Promise<Extension>> = {
  javascript: () =>
    import('@codemirror/lang-javascript').then((m) => m.javascript()),
  typescript: () =>
    import('@codemirror/lang-javascript').then((m) =>
      m.javascript({ typescript: true })
    ),
  tsx: () =>
    import('@codemirror/lang-javascript').then((m) =>
      m.javascript({ typescript: true, jsx: true })
    ),
  jsx: () =>
    import('@codemirror/lang-javascript').then((m) =>
      m.javascript({ jsx: true })
    ),
  python: () =>
    import('@codemirror/lang-python').then((m) => m.python()),
  rust: () =>
    import('@codemirror/lang-rust').then((m) => m.rust()),
  json: () =>
    import('@codemirror/lang-json').then((m) => m.json()),
  html: () =>
    import('@codemirror/lang-html').then((m) => m.html()),
  css: () =>
    import('@codemirror/lang-css').then((m) => m.css()),
  markdown: () =>
    import('@codemirror/lang-markdown').then((m) => m.markdown()),
  java: () =>
    import('@codemirror/lang-java').then((m) => m.java()),
  cpp: () =>
    import('@codemirror/lang-cpp').then((m) => m.cpp()),
  // C uses the C++ grammar — it parses plain C correctly
  c: () =>
    import('@codemirror/lang-cpp').then((m) => m.cpp()),
  php: () =>
    import('@codemirror/lang-php').then((m) => m.php()),
  sql: () =>
    import('@codemirror/lang-sql').then((m) => m.sql()),
  xml: () =>
    import('@codemirror/lang-xml').then((m) => m.xml()),
  yaml: () =>
    import('@codemirror/lang-yaml').then((m) => m.yaml()),
  // Legacy modes (stream-based parsers)
  go: () =>
    import('@codemirror/legacy-modes/mode/go').then((m) =>
      StreamLanguage.define(m.go)
    ),
  shell: () =>
    import('@codemirror/legacy-modes/mode/shell').then((m) =>
      StreamLanguage.define(m.shell)
    ),
  ruby: () =>
    import('@codemirror/legacy-modes/mode/ruby').then((m) =>
      StreamLanguage.define(m.ruby)
    ),
}

/**
 * Forge code editor powered by CodeMirror 6.
 *
 * Features:
 * - Syntax highlighting for 20+ languages (dynamic import)
 * - Auto-matching brackets, line numbers, code folding
 * - Tab indentation, active line highlight
 * - Virtual scrolling (handles large files well)
 * - Automatic fallback to plain textarea for very large files (>500KB)
 * - Theme sync with Forge dark/light mode
 */
export function CodeMirrorEditor({
  value,
  onChange,
  language = 'text',
  readOnly = false,
  wordWrap = true,
  className,
}: CodeMirrorEditorProps) {
  const { resolvedTheme } = useTheme()
  const [langExt, setLangExt] = useState<Extension | null>(null)
  const [langLoading, setLangLoading] = useState(false)

  const isLargeFile = value.length > LARGE_FILE_THRESHOLD

  // Dynamic import of language grammar
  useEffect(() => {
    if (language === 'text' || !LANG_IMPORTS[language]) {
      setLangExt(null)
      return
    }

    setLangLoading(true)
    let cancelled = false

    LANG_IMPORTS[language]()
      .then((ext) => {
        if (!cancelled) {
          setLangExt(ext)
          setLangLoading(false)
        }
      })
      .catch(() => {
        if (!cancelled) {
          setLangExt(null)
          setLangLoading(false)
        }
      })

    return () => {
      cancelled = true
    }
  }, [language])

  // Theme: Forge custom theme for dark, oneDark for light (as base)
  const theme = resolvedTheme === 'dark' ? forgeDark : forgeLight

  // Assemble extensions
  const extensions = useMemo(() => {
    const exts: Extension[] = []

    if (langExt) {
      exts.push(langExt)
    }

    if (wordWrap) {
      exts.push(EditorView.lineWrapping)
    }

    // Tab inserts spaces (2 spaces)
    exts.push(
      keymap.of([
        {
          key: 'Tab',
          run: (view) => {
            view.dispatch(
              view.state.update({
                changes: {
                  from: view.state.selection.main.from,
                  to: view.state.selection.main.to,
                  insert: '  ',
                },
                selection: {
                  anchor:
                    view.state.selection.main.from + 2,
                  head: view.state.selection.main.from + 2,
                },
              })
            )
            return true
          },
        },
        indentWithTab,
      ])
    )

    return exts
  }, [langExt, wordWrap])

  const handleChange = useCallback(
    (newValue: string) => {
      onChange(newValue)
    },
    [onChange]
  )

  // Fallback: very large files use the lightweight textarea
  if (isLargeFile) {
    return (
      <CodeEditor
        value={value}
        onChange={onChange}
        readOnly={readOnly}
        language={language}
        wordWrap={wordWrap}
        className={className}
      />
    )
  }

  return (
    <div
      className={cn(
        'relative h-full cm-editor-forge',
        className
      )}
    >
      {langLoading && language !== 'text' && (
        <div className="absolute top-2 right-2 z-10">
          <div className="w-3 h-3 rounded-full border-2 border-subtle border-t-indigo animate-spin" />
        </div>
      )}
      <ReactCodeMirror
        value={value}
        onChange={handleChange}
        theme={theme}
        editable={!readOnly}
        extensions={extensions}
        height="100%"
        basicSetup={{
          lineNumbers: true,
          highlightActiveLineGutter: true,
          highlightActiveLine: true,
          foldGutter: true,
          bracketMatching: true,
          closeBrackets: true,
          autocompletion: false, // disabled to keep it lightweight
          indentOnInput: true,
          searchKeymap: true,
          tabSize: 2,
        }}
        className="text-[13px] h-full"
      />
    </div>
  )
}
