import type { ReactNode } from 'react'
import { useEffect, useState } from 'react'
import { createPortal } from 'react-dom'

import type { BeforeMount } from '@monaco-editor/react'
import { Expand, Minimize2, Sparkles } from 'lucide-react'

import { LazyDiffEditor, LazyEditor } from '@/components/lazy-monaco'
import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'

export type MarkdownWorkbenchPanel = 'edit' | 'preview' | 'diff'
export type MarkdownWorkbenchMode = 'workspace' | 'polish'

type MarkdownWorkbenchProps = {
  value: string
  originalValue: string
  diffValue?: string
  path: string
  workspaceHeightClassName?: string
  readOnly?: boolean
  mode: MarkdownWorkbenchMode
  visiblePanels: MarkdownWorkbenchPanel[]
  availablePanels?: MarkdownWorkbenchPanel[]
  allowPolish?: boolean
  preview: ReactNode
  polishPanel?: ReactNode
  originalLabel?: string
  modifiedLabel?: string
  onChange: (value: string) => void
  onModeChange: (next: MarkdownWorkbenchMode) => void
  onVisiblePanelsChange: (next: MarkdownWorkbenchPanel[]) => void
}

export const editorTheme = 'termi-vscode'

const orderedWorkbenchPanels: MarkdownWorkbenchPanel[] = ['edit', 'preview', 'diff']

function formatPanelLabel(panel: MarkdownWorkbenchPanel) {
  switch (panel) {
    case 'preview':
      return '预览'
    case 'diff':
      return '改动对比'
    case 'edit':
    default:
      return '编辑'
  }
}

function resolveVisiblePanels(
  visiblePanels: MarkdownWorkbenchPanel[],
  availablePanels: MarkdownWorkbenchPanel[],
) {
  const orderedAvailablePanels = orderedWorkbenchPanels.filter((panel) =>
    availablePanels.includes(panel),
  )
  const nextPanels = orderedAvailablePanels.filter((panel) => visiblePanels.includes(panel))
  return nextPanels.length ? nextPanels : orderedAvailablePanels.slice(0, 1)
}

export const configureMonaco: BeforeMount = (monaco) => {
  monaco.editor.defineTheme(editorTheme, {
    base: 'vs-dark',
    inherit: true,
    rules: [
      { token: 'comment', foreground: '6A9955' },
      { token: 'keyword', foreground: 'C586C0' },
      { token: 'string', foreground: 'CE9178' },
      { token: 'number', foreground: 'B5CEA8' },
      { token: 'delimiter', foreground: 'D4D4D4' },
      { token: 'type.identifier', foreground: '4EC9B0' },
    ],
    colors: {
      'editor.background': '#1e1e1e',
      'editor.foreground': '#d4d4d4',
      'editor.lineHighlightBackground': '#2a2d2e',
      'editor.lineHighlightBorder': '#00000000',
      'editorCursor.foreground': '#aeafad',
      'editor.selectionBackground': '#264f78',
      'editor.inactiveSelectionBackground': '#3a3d41',
      'editorWhitespace.foreground': '#3b3b3b',
      'editorIndentGuide.background1': '#404040',
      'editorIndentGuide.activeBackground1': '#707070',
      'editorLineNumber.foreground': '#858585',
      'editorLineNumber.activeForeground': '#c6c6c6',
      'editorGutter.background': '#1e1e1e',
      'editorOverviewRuler.border': '#00000000',
      'diffEditor.insertedTextBackground': '#9ccc2c33',
      'diffEditor.removedTextBackground': '#ff6b6b2d',
      'diffEditor.insertedLineBackground': '#9ccc2c18',
      'diffEditor.removedLineBackground': '#ff6b6b18',
    },
  })
}

export const sharedOptions = {
  automaticLayout: true,
  fontFamily:
    '"JetBrains Mono", "Cascadia Code", "Fira Code", ui-monospace, SFMono-Regular, monospace',
  fontLigatures: true,
  fontSize: 14,
  lineHeight: 22,
  minimap: { enabled: false },
  padding: { top: 16, bottom: 16 },
  renderWhitespace: 'selection' as const,
  roundedSelection: false,
  scrollBeyondLastLine: false,
  smoothScrolling: true,
  tabSize: 2,
  wordWrap: 'on' as const,
}

export function MarkdownWorkbench({
  value,
  originalValue,
  diffValue,
  path,
  workspaceHeightClassName = 'h-[560px]',
  readOnly = false,
  mode,
  visiblePanels,
  availablePanels = ['edit', 'preview', 'diff'],
  allowPolish,
  preview,
  polishPanel,
  originalLabel = '基线版本',
  modifiedLabel = '目标版本',
  onChange,
  onModeChange,
  onVisiblePanelsChange,
}: MarkdownWorkbenchProps) {
  const [fullscreen, setFullscreen] = useState(false)
  const editorHeight = fullscreen ? 'h-[calc(100dvh-82px)]' : workspaceHeightClassName
  const diffContent = diffValue ?? value
  const polishEnabled = allowPolish ?? Boolean(polishPanel)
  const workspacePanels = resolveVisiblePanels(visiblePanels, availablePanels)
  const renderDiffSideBySide = workspacePanels.length < 3 || fullscreen

  useEffect(() => {
    if (!fullscreen) {
      return
    }

    const previousOverflow = document.body.style.overflow
    document.body.style.overflow = 'hidden'

    return () => {
      document.body.style.overflow = previousOverflow
    }
  }, [fullscreen])

  const togglePanel = (panel: MarkdownWorkbenchPanel) => {
    const currentPanels = resolveVisiblePanels(visiblePanels, availablePanels)
    const nextPanels = currentPanels.includes(panel)
      ? currentPanels.filter((item) => item !== panel)
      : orderedWorkbenchPanels.filter(
          (item) => availablePanels.includes(item) && (currentPanels.includes(item) || item === panel),
        )

    onVisiblePanelsChange(nextPanels.length ? nextPanels : availablePanels.slice(0, 1))

    if (mode !== 'workspace') {
      onModeChange('workspace')
    }
  }

  const workbench = (
    <div
      className={cn(
        'overflow-hidden rounded-[28px] border border-slate-800 bg-[#1e1e1e] shadow-[0_24px_60px_rgba(15,23,42,0.28)]',
        fullscreen && 'relative h-[100dvh] rounded-none border-0 shadow-none',
      )}
    >
      <div className="flex flex-col gap-3 border-b border-slate-800 bg-[#181818] px-5 py-4 lg:flex-row lg:items-center lg:justify-between">
        <div className="space-y-1">
          <div className="flex items-center gap-2">
            <span className="h-3 w-3 rounded-full bg-[#ff5f56]" />
            <span className="h-3 w-3 rounded-full bg-[#ffbd2e]" />
            <span className="h-3 w-3 rounded-full bg-[#27c93f]" />
          </div>
          <p className="font-mono text-xs text-slate-400">Markdown 工作台</p>
        </div>

        <div className="flex flex-wrap items-center gap-2">
          {availablePanels.map((panel) => {
            const active = mode === 'workspace' && workspacePanels.includes(panel)

            return (
              <Button
                key={panel}
                variant={active ? 'default' : 'outline'}
                size="sm"
                onClick={() => togglePanel(panel)}
                className={
                  active
                    ? 'bg-[#0e639c] text-white shadow-none hover:bg-[#1177bb]'
                    : 'border-slate-700 bg-[#202020] text-slate-200 hover:bg-[#292929] hover:text-white'
                }
              >
                {formatPanelLabel(panel)}
              </Button>
            )
          })}
          {polishEnabled ? (
            <Button
              variant={mode === 'polish' ? 'default' : 'outline'}
              size="sm"
              onClick={() => onModeChange(mode === 'polish' ? 'workspace' : 'polish')}
              className={
                mode === 'polish'
                  ? 'bg-[#0e639c] text-white shadow-none hover:bg-[#1177bb]'
                  : 'border-slate-700 bg-[#202020] text-slate-200 hover:bg-[#292929] hover:text-white'
              }
            >
              <Sparkles className="mr-1 h-4 w-4" />
              AI 润色
            </Button>
          ) : null}
          <Button
            variant="outline"
            size="sm"
            onClick={() => setFullscreen((current) => !current)}
            className="border-slate-700 bg-[#202020] text-slate-200 hover:bg-[#292929] hover:text-white"
          >
            {fullscreen ? (
              <>
                <Minimize2 className="mr-1 h-4 w-4" />
                退出全屏
              </>
            ) : (
              <>
                <Expand className="mr-1 h-4 w-4" />
                全屏
              </>
            )}
          </Button>
        </div>
      </div>

      <div className={editorHeight}>
        {mode === 'polish' ? (
          <div className="h-full bg-[#111111]">{polishPanel}</div>
        ) : (
          <div className="flex h-full flex-col bg-slate-900 xl:flex-row">
            {workspacePanels.map((panel, index) => (
              <section
                key={panel}
                className={cn(
                  'flex min-h-0 flex-1 flex-col bg-[#1b1b1b]',
                  index < workspacePanels.length - 1 &&
                    'border-b border-slate-800 xl:border-b-0 xl:border-r',
                )}
              >
                <div className="flex items-center justify-between border-b border-slate-800 bg-[#141414] px-4 py-2 text-[11px] uppercase tracking-[0.18em] text-slate-400">
                  <span>{formatPanelLabel(panel)}</span>
                  {panel === 'diff' ? (
                    <span>
                      {originalLabel} / {modifiedLabel}
                    </span>
                  ) : null}
                </div>

                {panel === 'edit' ? (
                  <div className="min-h-0 flex-1">
                    <LazyEditor
                      height="100%"
                      language="markdown"
                      path={path}
                      value={value}
                      keepCurrentModel
                      theme={editorTheme}
                      beforeMount={configureMonaco}
                      options={{
                        ...sharedOptions,
                        readOnly,
                        stickyScroll: { enabled: true },
                      }}
                      onChange={(next) => onChange(next ?? '')}
                    />
                  </div>
                ) : null}

                {panel === 'preview' ? (
                  <div className="min-h-0 flex-1 overflow-auto bg-[#141414]">{preview}</div>
                ) : null}

                {panel === 'diff' ? (
                  <div className="min-h-0 flex-1">
                    <LazyDiffEditor
                      height="100%"
                      language="markdown"
                      original={originalValue}
                      modified={diffContent}
                      originalModelPath={`${path}#saved`}
                      modifiedModelPath={`${path}#draft`}
                      keepCurrentOriginalModel
                      keepCurrentModifiedModel
                      theme={editorTheme}
                      beforeMount={configureMonaco}
                      options={{
                        ...sharedOptions,
                        originalEditable: false,
                        readOnly: true,
                        renderSideBySide: renderDiffSideBySide,
                      }}
                    />
                  </div>
                ) : null}
              </section>
            ))}
          </div>
        )}
      </div>
    </div>
  )

  if (!fullscreen) {
    return workbench
  }

  if (typeof document === 'undefined') {
    return workbench
  }

  return createPortal(
    <>
      <div className="fixed inset-0 z-[900] bg-slate-950/92 backdrop-blur-md" />
      <div className="fixed inset-0 z-[1000]">{workbench}</div>
    </>,
    document.body,
  )
}
