@preconcurrency import SDWebImage
import ExpoModulesCore

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

  // MARK: - SDImageLoader

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

  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 ThumbhashLoader is missing")
      completedBlock?(nil, nil, error, false)
      return nil
    }
    // The URI looks like this: thumbhash:/3OcRJYB4d3h\iIeHeEh3eIhw+j2w
    // ThumbHash may include slashes which could break the structure of the URL, so we replace them
    // with backslashes on the JS side and revert them back to slashes here, before generating the image.
    var thumbhash = url.pathComponents[1].replacingOccurrences(of: "\\", with: "/")

    // Thumbhashes with transparency cause the conversion to data to fail, padding the thumbhash string to correct length fixes that
    let remainder = thumbhash.count % 4
    if remainder > 0 {
      thumbhash = thumbhash.padding(toLength: thumbhash.count + 4 - remainder, withPad: "=", startingAt: 0)
    }

    guard !thumbhash.isEmpty, let thumbhashData = Data(base64Encoded: thumbhash, options: .ignoreUnknownCharacters) else {
      let error = makeNSError(description: "URL provided to ThumbhashLoader is invalid")
      completedBlock?(nil, nil, error, false)
      return nil
    }

    Task(priority: .high) {
      let image = image(fromThumbhash: thumbhashData)

      await MainActor.run {
        completedBlock?(image, nil, nil, true)
      }
    }
    return nil
  }

  func shouldBlockFailedURL(with url: URL, error: Error) -> Bool {
    return true
  }
}
