//
//  PasteService.swift
//  humand
//
//  Created by Oleksandr Shumihin on 8/9/25.
//  Copyright © 2025 Humand. All rights reserved.
//

import UIKit
import UniformTypeIdentifiers

struct ImageData {
  let bytes: Int
  let uri: String
}

struct ImageDataWithType {
  let data: Data
  let type: String
}

@objc(PasteService)
open class PasteService: NSObject {
  var candidates: [(uti: String, ext: String)] = [
    (UTType.png.identifier, "png"),
    (UTType.jpeg.identifier, "jpg"),
    (UTType.heic.identifier, "heic"),
    (UTType.heif.identifier, "heif"),
  ]

  open func pasteHandler(onData: @escaping ([String: Any]?) -> Void) {
    self.pasteHandler(onData: onData, defaultHandler: nil)
  }

  open func pasteHandler(onData: @escaping ([String: Any]?) -> Void, defaultHandler: (() -> Void)?) {
    let pb = UIPasteboard.general

    let defaultHandlerOnMain = defaultHandler != nil ? {
      DispatchQueue.main.async {
        defaultHandler?()
      }
    } : nil

    // MARK: process UIImage

    if let image = pb.image {
      paste(image: image) { data in
        onData(data)
      }
      return
    }

    // MARK: process url image

    if pb.hasURLs {
      if let url = pb.url {
        paste(url: url) { data in
          guard let data else {
            defaultHandlerOnMain?()
            return
          }
          onData(data)
        }
        return
      }
    }

    // MARK: process base64 image

    if pb.hasStrings {
      if let string = pb.string, string.hasPrefix("data:image/") {
        paste(base64: string) { data in
          guard let data else {
            defaultHandlerOnMain?()
            return
          }
          onData(data)
        }
        return
      }
    }

    defaultHandlerOnMain?()
  }

  private func paste(image: UIImage, callback: @escaping ([String: Any]?) -> Void) {
    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
      guard let self else { return }

      guard let data = self.getDataFromImage(pb: UIPasteboard.general) else { return }
      self.processData(data: data, callback: callback)
    }
  }

  private func paste(base64: String, callback: @escaping ([String: Any]?) -> Void) {
    let parts = base64.components(separatedBy: ",")
    guard parts.count == 2 else { return }

    let base64StringWithoutPrefix = parts[1]
    guard let data = Data(base64Encoded: base64StringWithoutPrefix) else {
      return
    }

    DispatchQueue.global(qos: .userInitiated).async { [weak self] in
      guard let self else { return }
      let preparedData = ImageDataWithType(data: data, type: "jpeg")
      self.processData(data: preparedData, callback: callback)
    }
  }

  private func paste(url: URL, callback: @escaping ([String: Any]?) -> Void) {
    var req = URLRequest(url: url)
    req.httpMethod = "HEAD"
    req.timeoutInterval = 0.3

    let onSkip = {
      DispatchQueue.main.async {
        callback(nil)
      }
    }

    Task {
      do {
        let (_, resp) = try await URLSession.shared.data(for: req)
        let mime = resp.mimeType?.lowercased()
        let http = resp as? HTTPURLResponse
        let ok = (200 ... 299).contains(http?.statusCode ?? -1)

        let isImage = ok && (mime?.hasPrefix("image/") == true)

        if isImage {
          // Download image
          var getReq = URLRequest(url: url)
          getReq.timeoutInterval = 3.0
          
          let (data, _) = try await URLSession.shared.data(for: getReq)

          guard !data.isEmpty else {
            onSkip()
            return
          }

          let preparedData = ImageDataWithType(data: data, type: "jpeg")

          self.processData(data: preparedData, callback: callback)
        } else {
          onSkip()
        }
      } catch {
        onSkip()
      }
    }
  }

  private func processData(data: ImageDataWithType, callback: @escaping ([String: Any]?) -> Void) {
    do {
      let imgData = try self.createLocalImage(imageData: data)
      DispatchQueue.main.async {
        callback(["uri": imgData.uri, "bytes": imgData.bytes])
      }
    }
    catch {
      DispatchQueue.main.async {
        callback(nil)
      }
    }
  }

  private func getDataFromImage(pb: UIPasteboard) -> ImageDataWithType? {
    for c in candidates {
      if let d = pb.data(forPasteboardType: c.uti) {
        return ImageDataWithType(data: d, type: c.ext)
      }
    }

    return nil
  }

  private func createLocalImage(imageData: ImageDataWithType) throws -> ImageData {
    let tempDirectory = FileManager.default.temporaryDirectory
    let fileName = UUID().uuidString + ".\(imageData.type)"
    let fileURL = tempDirectory.appendingPathComponent(fileName)
    try imageData.data.write(to: fileURL, options: .atomic)

    return ImageData(bytes: imageData.data.count, uri: fileURL.absoluteString)
  }
}
