import AdSupport
import AppTrackingTransparency
import Foundation
import React
import UIKit

@objc(LayersATT)
class LayersATT: NSObject {

  private static let missingUsageDescriptionCode = "ATT_USAGE_DESCRIPTION_MISSING"
  private static let zeroAdvertisingIdentifier = "00000000-0000-0000-0000-000000000000"

  private static var hasTrackingUsageDescription: Bool {
    guard
      let value = Bundle.main.object(
        forInfoDictionaryKey: "NSUserTrackingUsageDescription"
      ) as? String
    else {
      return false
    }
    return !value.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
  }

  @available(iOS 14.0, *)
  private static func statusString(
    _ status: ATTrackingManager.AuthorizationStatus
  ) -> String {
    switch status {
    case .notDetermined:
      return "not_determined"
    case .restricted:
      return "restricted"
    case .denied:
      return "denied"
    case .authorized:
      return "authorized"
    @unknown default:
      return "not_determined"
    }
  }

  private static func onMain(_ work: @escaping () -> Void) {
    if Thread.isMainThread {
      work()
    } else {
      DispatchQueue.main.async(execute: work)
    }
  }

  @objc
  func getTrackingAuthorizationStatus(
    _ resolve: @escaping (Any?) -> Void,
    rejecter reject: @escaping (String?, String?, Error?) -> Void
  ) {
    Self.onMain {
      guard #available(iOS 14.0, *) else {
        resolve("not_determined")
        return
      }
      resolve(Self.statusString(ATTrackingManager.trackingAuthorizationStatus))
    }
  }

  @objc
  func requestTrackingAuthorization(
    _ resolve: @escaping (Any?) -> Void,
    rejecter reject: @escaping (String?, String?, Error?) -> Void
  ) {
    Self.onMain {
      guard #available(iOS 14.0, *) else {
        resolve("not_determined")
        return
      }

      let current = ATTrackingManager.trackingAuthorizationStatus
      guard current == .notDetermined else {
        resolve(Self.statusString(current))
        return
      }

      guard Self.hasTrackingUsageDescription else {
        reject(
          Self.missingUsageDescriptionCode,
          "Cannot request App Tracking Transparency authorization because " +
            "NSUserTrackingUsageDescription is missing or empty in the app's Info.plist.",
          nil
        )
        return
      }

      ATTrackingManager.requestTrackingAuthorization { status in
        resolve(Self.statusString(status))
      }
    }
  }

  @objc
  func isATTAvailable(
    _ resolve: @escaping (Any?) -> Void,
    rejecter reject: @escaping (String?, String?, Error?) -> Void
  ) {
    Self.onMain {
      if #available(iOS 14.0, *) {
        resolve(true)
      } else {
        resolve(false)
      }
    }
  }

  @objc
  func getAdvertisingId(
    _ resolve: @escaping (Any?) -> Void,
    rejecter reject: @escaping (String?, String?, Error?) -> Void
  ) {
    Self.onMain {
      guard
        #available(iOS 14.0, *),
        ATTrackingManager.trackingAuthorizationStatus == .authorized
      else {
        resolve(nil)
        return
      }

      let identifier = ASIdentifierManager.shared().advertisingIdentifier.uuidString
      resolve(identifier == Self.zeroAdvertisingIdentifier ? nil : identifier)
    }
  }

  @objc
  func getVendorId(
    _ resolve: @escaping (Any?) -> Void,
    rejecter reject: @escaping (String?, String?, Error?) -> Void
  ) {
    Self.onMain {
      resolve(UIDevice.current.identifierForVendor?.uuidString)
    }
  }

  @objc
  static func requiresMainQueueSetup() -> Bool {
    return false
  }
}
