@preconcurrency import SDWebImage
import ExpoModulesCore

class BlurhashLoader: NSObject, SDImageLoader {
  typealias ImageLoaderCompletedBlock = @Sendable (UIImage?, Data?, (any Error)?, Bool) -> Void

  // MARK: - SDImageLoader

  func canRequestImage(for url: URL?) -> Bool {
    return url?.scheme == "blurhash"
  }

  func requestImage(
    with url: URL?,
    options: SDWebImageOptions = [],
    context: [SDWebImageContextOption: Any]?,
    progress progressBlock: SDImageLoaderProgressBlock?,
    completed completedBlock: ImageLoaderCompletedBlock? = nil
  ) -> SDWebImageOperation? {
    guard let url else {
      let error = makeNSError(description: "URL provided to BlurhashLoader is missing")
      completedBlock?(nil, nil, error, false)
      return nil
    }
    guard let source = context?[ImageView.contextSourceKey] as? ImageSource else {
      let error = makeNSError(description: "Image source was not provided to the context")
      completedBlock?(nil, nil, error, false)
      return nil
    }

    // The URI looks like this: blurhash:/WgF}G?az0fs.x[jat7xFRjNHt6s.4;oe-:RkVtkCi^Nbo|xZRjWB
    // Which means that the `pathComponents[0]` is `/` and we need to skip it to get the hash.
    let blurhash = url.pathComponents[1]
    let size = CGSize(width: source.width, height: source.height)

    Task(priority: .high) {
      let image = image(fromBlurhash: blurhash, size: size)

      await MainActor.run {
        if let image {
          completedBlock?(UIImage(cgImage: image), nil, nil, true)
        } else {
          let error = makeNSError(description: "Unable to generate an image from the given blurhash")
          completedBlock?(nil, nil, error, false)
        }
      }
    }
    return nil
  }

  func shouldBlockFailedURL(with url: URL, error: Error) -> Bool {
    // If the algorithm failed to generate an image from the url,
    // it's not possible that next time it will work :)
    return true
  }
}
