import { CodeEditor, type CodeEditorHandles } from '@cherrystudio/ui'
import { useMultiplePreferences, usePreference } from '@data/hooks/usePreference'
import { Icon } from '@iconify/react'
import { loggerService } from '@logger'
import type { ActionTool } from '@renderer/components/ActionTools'
import {
  CodeToolbar,
  useCopyTool,
  useDownloadTool,
  useExpandTool,
  useRunTool,
  useSaveTool,
  useSplitViewTool,
  useViewSourceTool,
  useWrapTool
} from '@renderer/components/CodeToolbar'
import CodeViewer from '@renderer/components/CodeViewer'
import ImageViewer from '@renderer/components/ImageViewer'
import type { BasicPreviewHandles } from '@renderer/components/Preview'
import { useCodeStyle } from '@renderer/hooks/useCodeStyle'
import { pyodideService } from '@renderer/services/PyodideService'
import { getExtensionByLanguage } from '@renderer/utils/codeLanguage'
import { getFileIconName } from '@renderer/utils/fileIconName'
import { extractHtmlTitle, getFileNameFromHtmlTitle } from '@renderer/utils/formats'
import { cn } from '@renderer/utils/style'
import dayjs from 'dayjs'
import React, { memo, startTransition, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'

import { MAX_COLLAPSED_CODE_HEIGHT, SPECIAL_VIEW_COMPONENTS, SPECIAL_VIEWS } from './constants'
import StatusBar from './StatusBar'
import type { ViewMode } from './types'

const logger = loggerService.withContext('CodeBlockView')

interface Props {
  children: string
  language: string
  onSave?: (newContent: string) => void
  editable?: boolean
}

/**
 * 代码块视图
 *
 * 视图类型：
 * - preview: 预览视图，其中非源代码的是特殊视图
 * - edit: 编辑视图
 *
 * 视图模式：
 * - source: 源代码视图模式
 * - special: 特殊视图模式（Mermaid、PlantUML、SVG）
 * - split: 分屏模式（源代码和特殊视图并排显示）
 *
 * 顶部 sticky 工具栏：
 * - quick 工具
 * - core 工具
 */
export const CodeBlockView: React.FC<Props> = memo(({ children, language, onSave, editable = true }) => {
  const { t } = useTranslation()

  const [codeExecutionEnabled] = usePreference('chat.code.execution.enabled')
  const [codeExecutionTimeoutMinutes] = usePreference('chat.code.execution.timeout_minutes')
  const [codeCollapsible] = usePreference('chat.code.collapsible')
  const [codeWrappable] = usePreference('chat.code.wrappable')
  const [codeImageTools] = usePreference('chat.code.image_tools')
  const [fontSize] = usePreference('chat.message.font_size')
  const [codeShowLineNumbers] = usePreference('chat.code.show_line_numbers')
  const [codeEditor] = useMultiplePreferences({
    enabled: 'chat.code.editor.enabled',
    autocompletion: 'chat.code.editor.autocompletion',
    foldGutter: 'chat.code.editor.fold_gutter',
    highlightActiveLine: 'chat.code.editor.highlight_active_line',
    keymap: 'chat.code.editor.keymap',
    themeLight: 'chat.code.editor.theme_light',
    themeDark: 'chat.code.editor.theme_dark'
  })

  const { activeCmTheme } = useCodeStyle()

  const [viewState, setViewState] = useState({
    mode: 'special' as ViewMode,
    previousMode: 'special' as ViewMode
  })
  const { mode: viewMode } = viewState

  const setViewMode = useCallback((newMode: ViewMode) => {
    setViewState((current) => ({
      mode: newMode,
      // 当新模式不是 'split' 时才更新
      previousMode: newMode !== 'split' ? newMode : current.previousMode
    }))
  }, [])

  const toggleSplitView = useCallback(() => {
    setViewState((current) => {
      // 如果当前是 split 模式，恢复到上一个模式
      if (current.mode === 'split') {
        return { ...current, mode: current.previousMode }
      }
      return { mode: 'split', previousMode: current.mode }
    })
  }, [])

  const [isRunning, setIsRunning] = useState(false)
  const [executionResult, setExecutionResult] = useState<{ text: string; image?: string } | null>(null)

  const [tools, setTools] = useState<ActionTool[]>([])
  const codeEditorEnabled = codeEditor.enabled && editable

  const isExecutable = useMemo(() => {
    return codeExecutionEnabled && language === 'python'
  }, [codeExecutionEnabled, language])

  const sourceViewRef = useRef<CodeEditorHandles>(null)
  const specialViewRef = useRef<BasicPreviewHandles>(null)

  const hasSpecialView = useMemo(() => SPECIAL_VIEWS.includes(language), [language])

  const isInSpecialView = useMemo(() => {
    return hasSpecialView && viewMode === 'special'
  }, [hasSpecialView, viewMode])

  const [expandOverride, setExpandOverride] = useState(!codeCollapsible)
  const [wrapOverride, setWrapOverride] = useState(codeWrappable)

  // 重置用户操作
  useEffect(() => {
    setExpandOverride(!codeCollapsible)
  }, [codeCollapsible])

  // 重置用户操作
  useEffect(() => {
    setWrapOverride(codeWrappable)
  }, [codeWrappable])

  const shouldExpand = useMemo(() => !codeCollapsible || expandOverride, [codeCollapsible, expandOverride])
  const shouldWrap = useMemo(() => codeWrappable && wrapOverride, [codeWrappable, wrapOverride])

  const [sourceScrollHeight, setSourceScrollHeight] = useState(0)
  const expandable = useMemo(() => {
    return codeCollapsible && sourceScrollHeight > MAX_COLLAPSED_CODE_HEIGHT
  }, [codeCollapsible, sourceScrollHeight])

  const handleHeightChange = useCallback((height: number) => {
    startTransition(() => {
      setSourceScrollHeight((prev) => (prev === height ? prev : height))
    })
  }, [])

  const handleCopySource = useCallback(async () => {
    try {
      // Prioritize getting content from editor, fallback to children
      const content = sourceViewRef.current?.getContent?.() ?? children
      await navigator.clipboard.writeText(content.trimEnd())
      window.toast.success(t('code_block.copy.success'))
    } catch (error) {
      logger.error('Failed to copy to clipboard:', { error })
      window.toast.error(t('code_block.copy.failed'))
    }
  }, [children, t])
  // Note: sourceViewRef not in deps because it's a stable ref,
  // and getContent reads content in real-time from editorViewRef.current.state.doc

  const handleDownloadSource = useCallback(() => {
    let fileName = ''

    // 尝试提取 HTML 标题
    if (language === 'html') {
      fileName = getFileNameFromHtmlTitle(extractHtmlTitle(children)) || ''
    }

    // 默认使用日期格式命名
    if (!fileName) {
      fileName = `${dayjs().format('YYYYMMDDHHmm')}`
    }

    const ext = getExtensionByLanguage(language)
    void window.api.file.save(`${fileName}${ext}`, children)
  }, [children, language])

  const handleRunScript = useCallback(() => {
    setIsRunning(true)
    setExecutionResult(null)

    pyodideService
      .runScript(children, {}, codeExecutionTimeoutMinutes * 60000)
      .then((result) => {
        setExecutionResult(result)
      })
      .catch((error) => {
        logger.error('Unexpected error:', error)
        setExecutionResult({
          text: `Unexpected error: ${error.message || 'Unknown error'}`
        })
      })
      .finally(() => {
        setIsRunning(false)
      })
  }, [children, codeExecutionTimeoutMinutes])

  const showPreviewTools = useMemo(() => {
    return viewMode !== 'source' && hasSpecialView
  }, [hasSpecialView, viewMode])

  const hasStatusBar = isExecutable && !!executionResult

  // 复制按钮
  useCopyTool({
    showPreviewTools,
    previewRef: specialViewRef,
    onCopySource: handleCopySource,
    setTools
  })

  // 下载按钮
  useDownloadTool({
    showPreviewTools,
    previewRef: specialViewRef,
    onDownloadSource: handleDownloadSource,
    setTools
  })

  // 特殊视图的编辑/查看源码按钮，在分屏模式下不可用
  useViewSourceTool({
    enabled: hasSpecialView,
    editable: codeEditorEnabled,
    viewMode,
    onViewModeChange: setViewMode,
    setTools
  })

  // 特殊视图存在时的分屏按钮
  useSplitViewTool({
    enabled: hasSpecialView,
    viewMode,
    onToggleSplitView: toggleSplitView,
    setTools
  })

  // 运行按钮
  useRunTool({
    enabled: isExecutable,
    isRunning,
    onRun: handleRunScript,
    setTools
  })

  // 源代码视图的展开/折叠按钮
  useExpandTool({
    enabled: !isInSpecialView,
    expanded: shouldExpand,
    expandable,
    toggle: useCallback(() => setExpandOverride((prev) => !prev), []),
    setTools
  })

  // 源代码视图的自动换行按钮
  useWrapTool({
    enabled: !isInSpecialView,
    wrapped: shouldWrap,
    wrappable: codeWrappable,
    toggle: useCallback(() => setWrapOverride((prev) => !prev), []),
    setTools
  })

  // 代码编辑器的保存按钮
  useSaveTool({
    enabled: codeEditorEnabled && !isInSpecialView,
    sourceViewRef,
    setTools
  })

  // 源代码视图组件
  const sourceView = useMemo(
    () =>
      codeEditorEnabled ? (
        <CodeEditor
          className="source-view"
          ref={sourceViewRef}
          theme={activeCmTheme}
          fontSize={fontSize - 1}
          value={children}
          language={language}
          onSave={onSave}
          onHeightChange={handleHeightChange}
          maxHeight={`${MAX_COLLAPSED_CODE_HEIGHT}px`}
          options={{ stream: true, lineNumbers: codeShowLineNumbers, ...codeEditor }}
          expanded={shouldExpand}
          wrapped={shouldWrap}
        />
      ) : (
        <CodeViewer
          className="source-view"
          value={children}
          language={language}
          onHeightChange={handleHeightChange}
          expanded={shouldExpand}
          wrapped={shouldWrap}
          maxHeight={`${MAX_COLLAPSED_CODE_HEIGHT}px`}
          onRequestExpand={codeCollapsible ? () => setExpandOverride(true) : undefined}
        />
      ),
    [
      activeCmTheme,
      children,
      codeCollapsible,
      codeEditor,
      codeEditorEnabled,
      codeShowLineNumbers,
      fontSize,
      handleHeightChange,
      language,
      onSave,
      shouldExpand,
      shouldWrap
    ]
  )

  // 特殊视图组件映射
  const specialView = useMemo(() => {
    const SpecialView = SPECIAL_VIEW_COMPONENTS[language as keyof typeof SPECIAL_VIEW_COMPONENTS]

    if (!SpecialView) return null

    return (
      <SpecialView ref={specialViewRef} enableToolbar={codeImageTools}>
        {children}
      </SpecialView>
    )
  }, [children, codeImageTools, language])

  const renderHeader = useMemo(() => {
    if (isInSpecialView) {
      return (
        <div className="mt-1.5 flex h-4 items-center rounded-t-lg bg-transparent px-2.5 font-bold text-foreground text-sm leading-none" />
      )
    }
    const ext = getExtensionByLanguage(language)
    const iconName = getFileIconName(`file${ext}`)
    return (
      <div className="flex h-8 items-center rounded-t-lg bg-muted px-2.5 font-bold text-foreground text-sm leading-none">
        <Icon icon={`material-icon-theme:${iconName}`} style={{ fontSize: '1.1em', marginRight: 6 }} />
        {language.toUpperCase()}
      </div>
    )
  }, [isInSpecialView, language])

  // 根据视图模式和语言选择组件，优先展示特殊视图，fallback是源代码视图
  const renderContent = useMemo(() => {
    const showSpecialView = !!specialView && ['special', 'split'].includes(viewMode)
    const showSourceView = !specialView || viewMode !== 'special'

    return (
      <div
        className={cn(
          'split-view-wrapper flex *:w-full *:flex-[1_1_auto]',
          !hasStatusBar && (showSpecialView && !showSourceView ? 'rounded-lg' : 'rounded-b-lg'),
          !hasStatusBar && '[&_.code-viewer]:rounded-[inherit]',
          showSpecialView &&
            showSourceView &&
            "before:-translate-x-1/2 relative before:absolute before:top-0 before:bottom-0 before:left-1/2 before:z-[1] before:w-px before:bg-muted before:content-['']"
        )}>
        {showSpecialView && specialView}
        {showSourceView && sourceView}
      </div>
    )
  }, [hasStatusBar, specialView, sourceView, viewMode])

  return (
    <div
      className={cn(
        'code-block relative w-full min-w-[35ch]',
        '[&_.code-toolbar]:transform-gpu [&_.code-toolbar]:opacity-0 [&_.code-toolbar]:transition-opacity [&_.code-toolbar]:duration-200 [&_.code-toolbar]:ease-in-out [&_.code-toolbar]:will-change-[opacity]',
        '[&:hover_.code-toolbar]:opacity-100 [&_.code-toolbar.show]:opacity-100',
        isInSpecialView
          ? '[&_.code-toolbar]:rounded-none [&_.code-toolbar]:bg-transparent'
          : '[&_.code-toolbar]:rounded-[4px] [&_.code-toolbar]:bg-muted'
      )}>
      {renderHeader}
      <CodeToolbar tools={tools} />
      {renderContent}
      {isExecutable && executionResult && (
        <StatusBar>
          {executionResult.text}
          {executionResult.image && (
            <ImageViewer src={executionResult.image} alt="Matplotlib plot" style={{ cursor: 'pointer' }} />
          )}
        </StatusBar>
      )}
    </div>
  )
})
