import { Icon } from "@iconify/react"
import type { NodeViewProps } from "@tiptap/react"
import { NodeViewWrapper } from "@tiptap/react"
import { useCallback, useEffect, useRef, useState } from "react"
import { isValidPosition } from "../../utils/tiptap-utils"

export interface FileItem {
  id: string
  file: File
  progress: number
  status: "uploading" | "success" | "error"
  url?: string
  abortController?: AbortController
}

export interface UploadOptions {
  maxSize: number
  limit: number
  accept: string
  upload: (
    file: File,
    onProgress: (event: { progress: number }) => void,
    signal: AbortSignal,
  ) => Promise<string>
  onSuccess?: (url: string) => void
  onError?: (error: Error) => void
}

/**
 * Custom hook for managing multiple file uploads with progress tracking and cancellation
 */
function useFileUpload(options: UploadOptions) {
  const [fileItems, setFileItems] = useState<FileItem[]>([])

  const uploadFile = async (file: File): Promise<string | null> => {
    if (file.size > options.maxSize) {
      const error = new Error(
        `File size exceeds maximum allowed (${options.maxSize / 1024 / 1024}MB)`,
      )
      options.onError?.(error)
      return null
    }

    const abortController = new AbortController()
    const fileId = crypto.randomUUID()

    const newFileItem: FileItem = {
      id: fileId,
      file,
      progress: 0,
      status: "uploading",
      abortController,
    }

    setFileItems(prev => [...prev, newFileItem])

    try {
      if (!options.upload) {
        throw new Error("Upload function is not defined")
      }

      const url = await options.upload(
        file,
        (event: { progress: number }) => {
          setFileItems(prev =>
            prev.map(item => (item.id === fileId ? { ...item, progress: event.progress } : item)),
          )
        },
        abortController.signal,
      )

      if (!url) throw new Error("Upload failed: No URL returned")

      if (!abortController.signal.aborted) {
        setFileItems(prev =>
          prev.map(item =>
            item.id === fileId ? { ...item, status: "success", url, progress: 100 } : item,
          ),
        )
        options.onSuccess?.(url)
        return url
      }

      return null
    } catch (error) {
      if (!abortController.signal.aborted) {
        setFileItems(prev =>
          prev.map(item => (item.id === fileId ? { ...item, status: "error", progress: 0 } : item)),
        )
        options.onError?.(error instanceof Error ? error : new Error("Upload failed"))
      }
      return null
    }
  }

  const uploadFiles = async (files: File[]): Promise<string[]> => {
    if (!files || files.length === 0) {
      options.onError?.(new Error("No files to upload"))
      return []
    }

    if (options.limit && files.length > options.limit) {
      options.onError?.(
        new Error(`Maximum ${options.limit} file${options.limit === 1 ? "" : "s"} allowed`),
      )
      return []
    }

    // Upload all files concurrently
    const uploadPromises = files.map(file => uploadFile(file))
    const results = await Promise.all(uploadPromises)

    // Filter out null results (failed uploads)
    return results.filter((url): url is string => url !== null)
  }

  const removeFileItem = (fileId: string) => {
    setFileItems(prev => {
      const fileToRemove = prev.find(item => item.id === fileId)
      if (fileToRemove?.abortController) {
        fileToRemove.abortController.abort()
      }
      if (fileToRemove?.url) {
        URL.revokeObjectURL(fileToRemove.url)
      }
      return prev.filter(item => item.id !== fileId)
    })
  }

  const clearAllFiles = () => {
    fileItems.forEach(item => {
      if (item.abortController) {
        item.abortController.abort()
      }
      if (item.url) {
        URL.revokeObjectURL(item.url)
      }
    })
    setFileItems([])
  }

  return {
    fileItems,
    uploadFiles,
    removeFileItem,
    clearAllFiles,
  }
}

interface ImageUploadPreviewProps {
  fileItem: FileItem
  onRemove: () => void
}

/**
 * Component that displays a preview of an uploading file with progress
 */
const ImageUploadPreview: React.FC<ImageUploadPreviewProps> = ({ fileItem, onRemove }) => {
  const formatFileSize = (bytes: number) => {
    if (bytes === 0) return "0 Bytes"
    const k = 1024
    const sizes = ["Bytes", "KB", "MB", "GB"]
    const i = Math.floor(Math.log(bytes) / Math.log(k))
    return `${parseFloat((bytes / k ** i).toFixed(2))} ${sizes[i]}`
  }

  return (
    <div className="tiptap-image-upload-preview">
      {fileItem.status === "uploading" && (
        <div className="tiptap-image-upload-progress" style={{ width: `${fileItem.progress}%` }} />
      )}

      <div className="tiptap-image-upload-preview-content">
        <div className="tiptap-image-upload-file-info">
          <div className="tiptap-image-upload-file-icon">
            <Icon icon="mdi:cloud-upload" className="size-6" />
          </div>
          <div className="tiptap-image-upload-details">
            <span className="tiptap-image-upload-text">{fileItem.file.name}</span>
            <span className="tiptap-image-upload-subtext">
              {formatFileSize(fileItem.file.size)}
            </span>
          </div>
        </div>
        <div className="tiptap-image-upload-actions">
          {fileItem.status === "uploading" && (
            <span className="tiptap-image-upload-progress-text">{fileItem.progress}%</span>
          )}
          <button
            type="button"
            className="tiptap-button-ghost"
            onClick={e => {
              e.stopPropagation()
              onRemove()
            }}
          >
            ✕
          </button>
        </div>
      </div>
    </div>
  )
}

export const ImageUploadNode: React.FC<NodeViewProps> = props => {
  const { accept, limit, maxSize, pastedFile } = props.node.attrs
  const inputRef = useRef<HTMLInputElement>(null)
  const extension = props.extension

  const uploadOptions: UploadOptions = {
    maxSize,
    limit,
    accept,
    upload: extension.options.upload,
    onSuccess: extension.options.onSuccess,
    onError: extension.options.onError,
  }

  const { fileItems, uploadFiles, removeFileItem, clearAllFiles } = useFileUpload(uploadOptions)

  // Auto-trigger file input when component mounts (when node is inserted)
  useEffect(() => {
    if (inputRef.current && fileItems.length === 0 && !inputRef.current.files?.length) {
      // Small delay to ensure the component is fully rendered
      const timer = setTimeout(() => {
        inputRef.current?.click()
      }, 100)
      return () => clearTimeout(timer)
    }
  }, [fileItems.length])

  const handleUpload = useCallback(
    async (files: File[]) => {
      const urls = await uploadFiles(files)

      if (urls.length > 0) {
        const pos = props.getPos()

        if (isValidPosition(pos)) {
          const imageNodes = urls.map((url, index) => {
            const filename = files[index]?.name.replace(/\.[^/.]+$/, "") || "unknown"
            return {
              type: "image",
              attrs: { src: url, alt: filename, title: filename },
            }
          })

          props.editor
            .chain()
            .focus()
            .deleteRange({ from: pos, to: pos + 1 })
            .insertContentAt(pos, imageNodes)
            .run()
        }
      }
    },
    [uploadFiles, props.getPos, props.editor],
  )

  const handleChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      const files = e.target.files
      if (!files || files.length === 0) {
        extension.options.onError?.(new Error("No file selected"))
        return
      }
      handleUpload(Array.from(files))
    },
    [extension.options, handleUpload],
  )

  // Handle pasted file from attributes
  useEffect(() => {
    if (pastedFile && fileItems.length === 0) {
      handleUpload([pastedFile])
    }
  }, [pastedFile, fileItems.length, handleUpload])

  const hasFiles = fileItems.length > 0

  return (
    <NodeViewWrapper className="tiptap-image-upload" tabIndex={0}>
      {hasFiles && (
        <div className="tiptap-image-upload-previews">
          {fileItems.length > 1 && (
            <div className="tiptap-image-upload-header">
              <span>Uploading {fileItems.length} files</span>
              <button
                type="button"
                className="tiptap-button-ghost"
                onClick={e => {
                  e.stopPropagation()
                  clearAllFiles()
                }}
              >
                Clear All
              </button>
            </div>
          )}
          {fileItems.map(fileItem => (
            <ImageUploadPreview
              key={fileItem.id}
              fileItem={fileItem}
              onRemove={() => removeFileItem(fileItem.id)}
            />
          ))}
        </div>
      )}

      <input
        ref={inputRef}
        name="file"
        accept={accept}
        type="file"
        multiple={limit > 1}
        onChange={handleChange}
        onClick={(e: React.MouseEvent<HTMLInputElement>) => e.stopPropagation()}
      />
    </NodeViewWrapper>
  )
}
