import { useCallback, useEffect, useRef, useMemo } from 'react'
import {
  EditorView, keymap,
  highlightActiveLine, highlightActiveLineGutter,
  lineNumbers,
} from '@codemirror/view'
import { EditorState, type Extension } from '@codemirror/state'
import { defaultKeymap, indentWithTab, undo, redo } from '@codemirror/commands'
import { syntaxHighlighting, indentUnit, HighlightStyle } from '@codemirror/language'
import { tags } from '@lezer/highlight'
import { json } from '@codemirror/lang-json'
import { javascript } from '@codemirror/lang-javascript'
import { markdown } from '@codemirror/lang-markdown'
import { html } from '@codemirror/lang-html'
import { css } from '@codemirror/lang-css'
import { rust } from '@codemirror/lang-rust'
import { python } from '@codemirror/lang-python'
import { useTabsStore } from '@/stores/useTabsStore'
import { useContextMenuStore } from '@/stores/useContextMenuStore'
import { useSettingsStore } from '@/stores/useSettingsStore'

function buildLanguageExtensions(language: string) {
  switch (language) {
    case 'typescript':
      return javascript({ typescript: true })
    case 'javascript':
      return javascript()
    case 'json':
      return json()
    case 'markdown':
      return markdown()
    case 'html':
      return html()
    case 'css':
      return css()
    case 'rust':
      return rust()
    case 'python':
      return python()
    default:
      return []
  }
}

const tokyoNightSyntax = HighlightStyle.define([
  { tag: tags.keyword, color: '#7aa2f7' },
  { tag: [tags.controlKeyword, tags.operatorKeyword], color: '#7aa2f7' },
  { tag: tags.modifier, color: '#7aa2f7' },
  { tag: tags.definitionKeyword, color: '#7aa2f7' },
  { tag: tags.moduleKeyword, color: '#7aa2f7' },
  { tag: tags.self, color: '#ff5370' },
  { tag: tags.atom, color: '#ff9e64' },
  { tag: tags.bool, color: '#ff9e64' },
  { tag: tags.null, color: '#ff9e64' },
  { tag: tags.number, color: '#ff9e64' },
  { tag: tags.string, color: '#9ece6a' },
  { tag: tags.character, color: '#9ece6a' },
  { tag: tags.regexp, color: '#89ddff' },
  { tag: tags.escape, color: '#bb9af7' },
  { tag: tags.typeName, color: '#e0af68' },
  { tag: tags.className, color: '#e0af68' },
  { tag: tags.namespace, color: '#89ddff' },
  { tag: tags.macroName, color: '#ff5370' },
  { tag: tags.propertyName, color: '#73daca' },
  { tag: tags.attributeName, color: '#73daca' },
  { tag: tags.variableName, color: '#c0caf5' },
  { tag: tags.labelName, color: '#c0caf5' },
  { tag: tags.operator, color: '#89ddff' },
  { tag: tags.bracket, color: '#a9b1d6' },
  { tag: tags.angleBracket, color: '#89ddff' },
  { tag: tags.paren, color: '#a9b1d6' },
  { tag: tags.brace, color: '#a9b1d6' },
  { tag: tags.squareBracket, color: '#a9b1d6' },
  { tag: tags.separator, color: '#a9b1d6' },
  { tag: tags.punctuation, color: '#a9b1d6' },
  { tag: tags.comment, color: '#565f89', fontStyle: 'italic' },
  { tag: tags.lineComment, color: '#565f89', fontStyle: 'italic' },
  { tag: tags.blockComment, color: '#565f89', fontStyle: 'italic' },
  { tag: tags.docComment, color: '#565f89', fontStyle: 'italic' },
  { tag: tags.docString, color: '#565f89' },
  { tag: tags.meta, color: '#565f89' },
  { tag: tags.content, color: '#c0caf5' },
  { tag: tags.heading, color: '#7aa2f7', fontWeight: '600' },
  { tag: tags.strong, color: '#c0caf5', fontWeight: '700' },
  { tag: tags.emphasis, color: '#c0caf5', fontStyle: 'italic' },
  { tag: tags.strikethrough, color: '#565f89', textDecoration: 'line-through' },
  { tag: tags.link, color: '#73daca', textDecoration: 'underline' },
  { tag: tags.url, color: '#73daca', textDecoration: 'underline' },
  { tag: tags.list, color: '#7dcfff' },
  { tag: tags.quote, color: '#9ece6a' },
  { tag: tags.inserted, color: '#9ece6a' },
  { tag: tags.deleted, color: '#ff5370' },
  { tag: tags.changed, color: '#e0af68' },
  { tag: tags.invalid, color: '#ff5370' },
  { tag: tags.definition(tags.variableName), color: '#c0caf5' },
  { tag: tags.definition(tags.propertyName), color: '#73daca' },
  { tag: tags.function(tags.variableName), color: '#7dcfff' },
  { tag: tags.definition(tags.function(tags.variableName)), color: '#7dcfff' },
  { tag: tags.local(tags.variableName), color: '#c0caf5' },
  { tag: tags.special(tags.variableName), color: '#ff5370' },
  { tag: tags.special(tags.string), color: '#bb9af7' },
])

function buildEditorTheme(settings: {
  fontSize: number
  fontFamily: string
  ruler: boolean
  rulerColumn: number
}) {
  const rulerCSS = settings.ruler
    ? `linear-gradient(90deg, transparent calc(${settings.rulerColumn}ch + 0.5px), var(--color-ruler) calc(${settings.rulerColumn}ch + 0.5px), var(--color-ruler) calc(${settings.rulerColumn + 0.5}ch + 0.5px), transparent calc(${settings.rulerColumn + 0.5}ch + 0.5px))`
    : 'none'

  return EditorView.theme({
    '&': {
      fontSize: `${settings.fontSize}px`,
      fontFamily: `'${settings.fontFamily}', 'Cascadia Code', 'Fira Code', Consolas, monospace`,
      height: '100%',
      backgroundColor: 'var(--color-editor-bg)',
    },
    '.cm-scroller': {
      fontFamily: `'${settings.fontFamily}', 'Cascadia Code', 'Fira Code', Consolas, monospace`,
    },
    '.cm-content': {
      padding: '16px 0',
      caretColor: 'var(--color-accent)',
      backgroundImage: rulerCSS,
      backgroundPosition: '0 0',
      backgroundRepeat: 'no-repeat',
    },
    '.cm-line': {
      padding: '0 20px',
    },
    '.cm-gutters': {
      backgroundColor: 'var(--color-editor-surface)',
      borderRight: '1px solid var(--color-panel-border)',
      color: 'var(--color-text-muted)',
    },
    '.cm-lineNumbers .cm-gutterElement': {
      padding: '0 12px 0 16px',
      fontSize: '12px',
      fontFamily: `'${settings.fontFamily}', 'Cascadia Code', 'Fira Code', Consolas, monospace`,
      lineHeight: '1.6',
    },
    '.cm-activeLineGutter': {
      backgroundColor: 'var(--color-editor-line)',
      color: 'var(--color-text-primary)',
      fontWeight: '600',
    },
    '.cm-activeLine': {
      backgroundColor: 'rgba(12,12,14,0.5)',
    },
    '.cm-cursor': {
      borderLeftWidth: '2px',
      borderLeftColor: 'var(--color-accent)',
    },
    '&.cm-focused .cm-cursor': {
      borderLeftColor: 'var(--color-accent)',
    },
    '.cm-selectionBackground': {
      backgroundColor: 'rgba(240,160,48,0.2) !important',
    },
    '&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground': {
      backgroundColor: 'rgba(240,160,48,0.25) !important',
    },
    '.cm-selectionMatch': {
      backgroundColor: 'rgba(240,160,48,0.1) !important',
    },
  })
}

export default function YaceEditor() {
  const editorRef = useRef<HTMLDivElement>(null)
  const viewRef = useRef<EditorView | null>(null)
  const containerRef = useRef<HTMLDivElement>(null)
  const updateTimeoutRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined)

  const activeTab = useTabsStore((s) =>
    s.activeTabId ? s.tabs.find((t) => t.id === s.activeTabId) ?? null : null
  )
  const updateContent = useTabsStore((s) => s.updateContent)
  const setCursorPosition = useTabsStore((s) => s.setCursorPosition)
  const openContextMenu = useContextMenuStore((s) => s.open)

  const fontSize = useSettingsStore((s) => s.fontSize)
  const tabSize = useSettingsStore((s) => s.tabSize)
  const fontFamily = useSettingsStore((s) => s.fontFamily)
  const wordWrap = useSettingsStore((s) => s.wordWrap)
  const lineNumbersEnabled = useSettingsStore((s) => s.lineNumbers)
  const ruler = useSettingsStore((s) => s.ruler)
  const rulerColumn = useSettingsStore((s) => s.rulerColumn)

  const settings = { fontSize, tabSize, fontFamily, wordWrap, lineNumbers: lineNumbersEnabled, ruler, rulerColumn }

  const editorItems = useMemo(() => [
    { id: 'undo', label: 'Undo', shortcut: 'Ctrl+Z', icon: 'undo' as const, action: () => viewRef.current && undo(viewRef.current) },
    { id: 'redo', label: 'Redo', shortcut: 'Ctrl+Shift+Z', icon: 'redo' as const, action: () => viewRef.current && redo(viewRef.current) },
    { id: 'sep1', label: '', separator: true, action: () => {} },
    { id: 'cut', label: 'Cut', shortcut: 'Ctrl+X', icon: 'cut' as const, action: () => { document.execCommand('cut') } },
    { id: 'copy', label: 'Copy', shortcut: 'Ctrl+C', icon: 'copy' as const, action: () => { document.execCommand('copy') } },
    { id: 'paste', label: 'Paste', shortcut: 'Ctrl+V', icon: 'paste' as const, action: () => { navigator.clipboard.readText().then((t) => viewRef.current?.dispatch({ changes: { from: viewRef.current.state.selection.main.head, insert: t } })) } },
    { id: 'sep2', label: '', separator: true, action: () => {} },
    { id: 'selectAll', label: 'Select All', shortcut: 'Ctrl+A', icon: 'selectAll' as const, action: () => { viewRef.current?.dispatch({ selection: { anchor: 0, head: viewRef.current.state.doc.length } }) } },
  ], [])

  const handleContextMenu = useCallback((e: MouseEvent) => {
    e.preventDefault()
    openContextMenu(e.clientX, e.clientY, editorItems)
  }, [openContextMenu, editorItems])

  const handleChange = useCallback(
    (content: string) => {
      if (updateTimeoutRef.current) clearTimeout(updateTimeoutRef.current)
      updateTimeoutRef.current = setTimeout(() => {
        const id = useTabsStore.getState().activeTabId
        if (id) updateContent(id, content)
      }, 300)
    },
    [updateContent]
  )

  useEffect(() => {
    if (!editorRef.current) return

    if (viewRef.current) viewRef.current.destroy()

    const languageExtensions = activeTab
      ? buildLanguageExtensions(activeTab.language)
      : []

    const updateListener = EditorView.updateListener.of((update) => {
      if (update.docChanged) {
        handleChange(update.state.doc.toString())
      }
      if (update.selectionSet) {
        const pos = update.state.selection.main.head
        const line = update.state.doc.lineAt(pos)
        setCursorPosition(line.number, pos - line.from + 1)
      }
    })

    const extensions: Extension[] = [
      ...(lineNumbersEnabled ? [lineNumbers(), highlightActiveLineGutter()] : [highlightActiveLineGutter()]),
      highlightActiveLine(),
      updateListener,
      keymap.of([...defaultKeymap, indentWithTab]),
      syntaxHighlighting(tokyoNightSyntax),
      buildEditorTheme(settings),
      EditorState.tabSize.of(tabSize),
      indentUnit.of(' '.repeat(tabSize)),
      languageExtensions,
    ]

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

    const state = EditorState.create({
      doc: activeTab?.content ?? '',
      extensions: extensions.flat(),
    })

    viewRef.current = new EditorView({
      state,
      parent: editorRef.current,
    })

    return () => {
      if (updateTimeoutRef.current) clearTimeout(updateTimeoutRef.current)
      viewRef.current?.destroy()
      viewRef.current = null
    }
  }, [activeTab?.id, activeTab?.language, fontSize, tabSize, fontFamily, wordWrap, lineNumbersEnabled, ruler, rulerColumn])

  useEffect(() => {
    const el = containerRef.current
    if (!el) return
    el.addEventListener('contextmenu', handleContextMenu)
    return () => el.removeEventListener('contextmenu', handleContextMenu)
  }, [handleContextMenu])

  if (!activeTab) {
    return null
  }

  return (
    <div ref={containerRef} className="flex-1 overflow-hidden bg-editor-bg">
      <div ref={editorRef} className="h-full" />
    </div>
  )
}
