import Foundation
import React

@available(iOS 15.0, *)
@objc(PurchaseKit)
final class PurchaseKit: NSObject {
  private lazy var storeKit = StoreKitObservedModel()

  @objc(products:withResolver:withRejecter:)
  public func products(input: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
    let inputIds = input["ids"] as? [String]

    Task {
      if let ids = inputIds {
        do {
          let products = try await storeKit.products(ids: ids)
          let map = products.map { encoder(parseProduct($0)) }

          resolve(map)
        } catch let error as PurchaseKitError {
          reject("\(error.code)", error.message, error)
        } catch {
          reject("\(ErrorCode.UNKNOWN)", "An error happened when getting theses products", error)
        }
      }
    }
  }

  @objc(entitlements:withRejecter:)
  public func entitlements(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
    Task {
      do {
        let result = try await storeKit.entitlements()
        let map = result.map { encoder(parseTransaction($0)) }

        resolve(map)
      } catch {
        reject("\(ErrorCode.UNKNOWN)", "An error happened when getting this entitlement", error)
      }
    }
  }

  @objc(purchase:withResolver:withRejecter:)
  public func purchase(input: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
    let inputId = input["id"] as? String
    let inputUserId = input["userId"] as? String

    Task {
      if let id = inputId, let userId = inputUserId {
        do {
          let transaction = try await storeKit.purchase(
            id: id,
            userId: userId
          )

          if let transaction = transaction {
            let payload = encoder(parseTransaction(transaction))

            resolve(payload)
          }
        } catch let error as PurchaseKitError {
          reject("\(error.code)", error.message, error)
        } catch {
          reject("\(ErrorCode.UNKNOWN)", "An error happened when purchasing this product", error)
        }
      } else {
        reject("\(ErrorCode.INPUT_VALIDATION)", "Missing id or userId", nil)
      }
    }
  }

  @objc(finishTransaction:withResolver:withRejecter:)
  public func finsihTransaction(input: NSDictionary, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
    let inputId = input["id"] as? String

    Task {
      if let id = inputId {
        do {
          try await storeKit.finishTransaction(
            id: id
          )

          resolve(nil)
        } catch let error as PurchaseKitError {
          reject("\(error.code)", error.message, error)
        } catch {
          reject("\(ErrorCode.UNKNOWN)", "An error happened when finishing this transaction", error)
        }
      } else {
        reject("\(ErrorCode.INPUT_VALIDATION)", "Missing id", nil)
      }
    }
  }

  @objc(syncPurchases:withRejecter:)
  public func syncPurchases(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
    Task {
      let result = await storeKit.syncPurchases()

      resolve(result)
    }
  }
}
