import type { TaskArtifact } from '../../service/type'
import { memo, useEffect, useMemo, useState, forwardRef, useImperativeHandle } from 'react'
import { ErrorBoundary } from '../ui/error-boundary'
import {
  DominoProvider,
  createDominoStore,
  useDominoStoreInstance,
  type DominoStore,
  type ImageElement,
} from '../canvas'
import { usePersistence } from '../../hooks/use-persistence'
import { getImageUrl, getImageDimension } from '../../utils/helper'
import { useLayout } from '../../hooks/use-layout'
import { cn } from '@/utils/cn'
import DomiBoard, { type DomiBoardProps } from './domi-board'

export interface ImageEditorProps extends DomiBoardProps {
  /**
   * 任务ID/会话ID
   */
  taskId: string
  /**
   * 当前产物，更新这个值可能会追加到编辑器中
   */
  currentArtifact?: TaskArtifact
}

export interface ImageEditorHandle {
  /**
   * 批量添加产物到编辑器，并自动布局
   * @param artifacts 产物列表
   * @param options.autoFocus 是否在添加完成后聚焦到最后一个产物，默认为 true
   */
  addArtifacts: (
    artifacts: TaskArtifact[],
    options?: { autoFocus?: boolean },
  ) => Promise<void>
}

const storeRegistry = new Map<string, DominoStore>()

function getOrCreateStore(taskId: string) {
  if (!storeRegistry.has(taskId)) {
    storeRegistry.set(
      taskId,
      createDominoStore({ viewport: { x: 0, y: 0, scale: 0.25 } }),
    )
  }
  return storeRegistry.get(taskId)!
}

const EditorCanvasManager = memo(
  forwardRef<ImageEditorHandle, ImageEditorProps>((props, ref) => {
    const { className, currentArtifact, taskId, ...rest } = props
    const [loading, setLoading] = useState(!!taskId)
    const { loadBoard, initialized } = usePersistence({ taskId, setLoading })
    const store = useDominoStoreInstance()

    const colMaxWidth = props.colMaxWidth || 7000
    const colGap = props.colGap || 40
    const rowGap = props.rowGap || 120

    const layoutOptions = useMemo(
      () => ({
        maxWidth: colMaxWidth,
        colGap,
        rowGap,
        padding: { top: 80, left: 80, bottom: 80, right: 80 },
      }),
      [colMaxWidth, colGap, rowGap],
    )

    const { addElementToFlow } = useLayout(layoutOptions)

    const addArtifacts = useMemo(
      () =>
        async (
          artifacts: TaskArtifact[],
          options: { autoFocus?: boolean } = { autoFocus: true },
        ) => {
          let lastId: string | null = null
          for (const artifact of artifacts) {
            const id = artifact.sandbox_path || artifact.path
            lastId = id

            const { elements } = store.getState()
            if (elements[id]) continue

            let url = artifact.url
            if (!url) {
              try {
                url = await getImageUrl(taskId, artifact.path)
              } catch (e) {
                console.warn('Failed to get image url', e)
                continue
              }
            }

            if (!url) continue

            try {
              const { width = 0, height = 0 } = await getImageDimension(url)
              if (width === 0 || height === 0) continue

              const newImage: ImageElement = {
                id,
                type: 'image',
                x: 0,
                y: 0,
                width,
                height,
                originalWidth: width,
                originalHeight: height,
                src: url,
                fileName:
                  (artifact.file_name || artifact.path).split('/').pop() ||
                  'image.jpg',
                rotation: 0,
                data: {
                  path: id,
                },
              }
              addElementToFlow(newImage)
            } catch (e) {
              console.warn('Failed to add artifact to board', e)
            }
          }

          if (options.autoFocus && lastId) {
            store.getState().setFocusedElementId(lastId)
          }
        },
      [taskId, store, addElementToFlow],
    )

    useImperativeHandle(ref, () => ({
      addArtifacts,
    }))

    useEffect(() => {
      const init = async () => {
        await loadBoard()
      }
      init()
    }, [loadBoard])

    // 处理外部传入的当前产物：自动添加到画板并聚焦
    useEffect(() => {
      if (!initialized || !currentArtifact) return

      const id = currentArtifact.sandbox_path || currentArtifact.path

      const handleFocusOrAdd = async () => {
        const { elements: currentElements, setFocusedElementId } =
          store.getState()
        if (!currentElements[id]) {
          const { url: artifactUrl, file_name, path } = currentArtifact
          let url = artifactUrl

          if (!url) {
            try {
              url = await getImageUrl(taskId, path)
            } catch (e) {
              console.warn('Failed to get image url', e)
            }
          }

          if (!url) {
            console.warn('Failed to get image url')
            return
          }

          try {
            const { width = 0, height = 0 } = await getImageDimension(url)
            if (width === 0 || height === 0) throw new Error('Image size is 0')
            addElementToFlow({
              id,
              type: 'image',
              x: 0,
              y: 0,
              width,
              height,
              originalWidth: width,
              originalHeight: height,
              src: url,
              fileName: (file_name || path).split('/').pop() || 'image.jpg',
              rotation: 0,
              data: {
                path: id,
              },
            })
          } catch (e) {
            console.warn('Failed to add current artifact to board', e)
          }
        }

        setFocusedElementId(id)
      }

      handleFocusOrAdd()
    }, [initialized, currentArtifact, addElementToFlow, taskId, store])

    return (
      <DomiBoard
        {...rest}
        className={cn('image-editor-sdk-root', className)}
        loading={loading}
        taskId={taskId}
      />
    )
  })
)

export const ImageEditor = memo(
  forwardRef<ImageEditorHandle, ImageEditorProps>((props, ref) => {
    const { taskId } = props

    const store = useMemo(() => getOrCreateStore(taskId), [taskId])

    if (!taskId) return null

    return (
      <ErrorBoundary>
        <DominoProvider store={store}>
          <EditorCanvasManager {...props} ref={ref} />
        </DominoProvider>
      </ErrorBoundary>
    )
  })
)

export default ImageEditor
