import { useState, useEffect, useContext, useRef, memo } from 'react'
import { Flex, Card } from 'antd'
import { FileOutlined } from '@ant-design/icons'
import { decode } from 'blurhash'
import PropTypes from 'prop-types'
import { ApiServerContext } from '../context/ApiServerContext'

const getFileId = (file) =>
  file?._id || (typeof file === 'string' ? file : null)

const getBlurHash = (file) => file?.metaData?.blurHash || null

const Thumbnail = function Thumbnail({
  file,
  size = 64,
  alt = '',
  fallback = null,
  style = {},
  className
}) {
  const { fetchFileThumbnail } = useContext(ApiServerContext)
  const fetchFileThumbnailRef = useRef(fetchFileThumbnail)
  fetchFileThumbnailRef.current = fetchFileThumbnail

  const fileId = getFileId(file)
  const blurHash = getBlurHash(file)

  const [thumbnailUrl, setThumbnailUrl] = useState(null)
  const [blurHashUrl, setBlurHashUrl] = useState(null)
  const thumbnailUrlRef = useRef(null)
  const loadedKeyRef = useRef(null)

  const imageStyle = {
    width: size,
    height: size,
    borderRadius: '5px',
    objectFit: 'cover',
    ...style
  }

  const cardStyle = {
    borderRadius: '5px',
    width: size,
    height: size,
    ...style
  }

  useEffect(() => {
    if (!blurHash) {
      setBlurHashUrl(null)
      return
    }

    try {
      const pixels = decode(blurHash, 32, 32)
      const canvas = document.createElement('canvas')
      canvas.width = 32
      canvas.height = 32
      const ctx = canvas.getContext('2d')
      const imageData = ctx.createImageData(32, 32)
      imageData.data.set(pixels)
      ctx.putImageData(imageData, 0, 0)
      setBlurHashUrl(canvas.toDataURL())
    } catch {
      setBlurHashUrl(null)
    }
  }, [blurHash])

  useEffect(() => {
    if (!fileId) {
      if (thumbnailUrlRef.current) {
        URL.revokeObjectURL(thumbnailUrlRef.current)
        thumbnailUrlRef.current = null
      }
      loadedKeyRef.current = null
      setThumbnailUrl(null)
      return
    }

    const loadKey = `${fileId}-${size}`
    if (loadedKeyRef.current === loadKey && thumbnailUrlRef.current) {
      return
    }

    let cancelled = false
    const fileObject =
      typeof file === 'object' && file !== null ? file : { _id: fileId }

    const loadThumbnail = async () => {
      try {
        const fileURL = await fetchFileThumbnailRef.current(fileObject, size)
        if (!cancelled) {
          if (thumbnailUrlRef.current) {
            URL.revokeObjectURL(thumbnailUrlRef.current)
          }
          thumbnailUrlRef.current = fileURL || null
          loadedKeyRef.current = loadKey
          setThumbnailUrl(fileURL || null)
        } else if (fileURL) {
          URL.revokeObjectURL(fileURL)
        }
      } catch {
        if (!cancelled) {
          setThumbnailUrl(null)
        }
      }
    }

    loadThumbnail()

    return () => {
      cancelled = true
    }
  }, [file, fileId, size])

  useEffect(() => {
    return () => {
      if (thumbnailUrlRef.current) {
        URL.revokeObjectURL(thumbnailUrlRef.current)
        thumbnailUrlRef.current = null
      }
      loadedKeyRef.current = null
    }
  }, [])

  if (thumbnailUrl) {
    return (
      <img
        src={thumbnailUrl}
        alt={alt}
        className={className}
        style={imageStyle}
      />
    )
  }

  if (fileId && blurHashUrl) {
    return (
      <img
        src={blurHashUrl}
        alt=''
        aria-hidden
        className={className}
        style={imageStyle}
      />
    )
  }

  return (
    <Card
      className={className}
      style={cardStyle}
      styles={{ body: { padding: 0, height: '100%', width: style?.width } }}
    >
      <Flex justify='center' align='center' style={{ height: '100%' }}>
        {fallback || (
          <FileOutlined
            style={{
              fontSize: Math.round(size * 0.22),
              color: 'var(--color-text-secondary)'
            }}
          />
        )}
      </Flex>
    </Card>
  )
}

Thumbnail.propTypes = {
  file: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
  size: PropTypes.oneOf([64, 128, 256]),
  alt: PropTypes.string,
  fallback: PropTypes.node,
  style: PropTypes.object,
  className: PropTypes.string
}

const areEqual = (prevProps, nextProps) => {
  return (
    getFileId(prevProps.file) === getFileId(nextProps.file) &&
    getBlurHash(prevProps.file) === getBlurHash(nextProps.file) &&
    prevProps.size === nextProps.size &&
    prevProps.alt === nextProps.alt &&
    JSON.stringify(prevProps.style) === JSON.stringify(nextProps.style)
  )
}

export default memo(Thumbnail, areEqual)
