/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

/**
 Contains App Link metadata relevant for navigation on this device
 derived from the HTML at a given URL.
 */
@objcMembers
@objc(FBSDKAppLink)
public final class AppLink: NSObject, _AppLinkProtocol {

  /// The URL from which this FBSDKAppLink was derived
  public let sourceURL: URL?

  /**
   The ordered list of targets applicable to this platform that will be used
   for navigation.
   */
  public let targets: [AppLinkTargetProtocol]

  /// The fallback web URL to use if no targets are installed on this device.
  public let webURL: URL?

  /**
   Internal property exposed to facilitate transition to Swift.
   API Subject to change or removal without warning. Do not use.

   > Warning: INTERNAL - DO NOT USE
   */
  public var isBackToReferrer: Bool

  /**
   Creates an AppLink with the given list of AppLinkTargets and target URL.

   Generally, this will only be used by implementers of the AppLinkResolving protocol,
   as these implementers will produce App Link metadata for a given URL.

   - Parameters:
      - sourceURL: The *URL* from which this App Link is derived.
      - targets: An ordered list of AppLinkTargets for this platform derived from App Link metadata.
      - webURL: The fallback web URL, if any, for the app link.
   */
  @objc(initWithSourceURL:targets:webURL:)
  public convenience init(sourceURL: URL?, targets: [AppLinkTargetProtocol], webURL: URL?) {
    self.init(sourceURL: sourceURL, targets: targets, webURL: webURL, isBackToReferrer: false)
  }

  /**
   Creates an AppLink with the given list of AppLinkTargets and target URL.

   Generally, this will only be used by implementers of the AppLinkResolving protocol,
   as these implementers will produce App Link metadata for a given URL.

   - Parameters:
      - sourceURL: The *URL* from which this App Link is derived.
      - targets: An ordered list of AppLinkTargets for this platform derived from App Link metadata.
      - webURL: The fallback web URL, if any, for the app link.
   */
  @available(
    *,
    deprecated,
    message: """
      Please use designated init to instantiate an AppLink. This method will be removed in future releases."
      """
  )
  @objc(appLinkWithSourceURL:targets:webURL:)
  public static func appLink(sourceURL: URL?, targets: [AppLinkTargetProtocol], webURL: URL?) -> _AppLinkProtocol {
    AppLink(sourceURL: sourceURL, targets: targets, webURL: webURL, isBackToReferrer: false)
  }

  /**
   Internal method exposed to facilitate transition to Swift.
   API Subject to change or removal without warning. Do not use.

   > Warning: INTERNAL - DO NOT USE
   */
  @objc(initWithSourceURL:targets:webURL:isBackToReferrer:)
  public init(sourceURL: URL?, targets: [AppLinkTargetProtocol], webURL: URL?, isBackToReferrer: Bool) {
    self.sourceURL = sourceURL
    self.targets = targets
    self.webURL = webURL
    self.isBackToReferrer = isBackToReferrer
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

/**
 Represents a pending request to navigate to an app link. Instead of simplying opening a URL, you can build custom requests with additional navigation and app data attached to them by creating an `AppLinkNavigation`.
 */
@objcMembers
@objc(FBSDKAppLinkNavigation)
@available(iOSApplicationExtension, unavailable, message: "Not available in app extension")
public final class AppLinkNavigation: NSObject {

  @nonobjc
  @available(
    *,
    deprecated,
    message: """
      This property is deprecated and will be removed in the next major release. \
      Use `defaultResolver` instead.
      """
  )

  public static var `default`: AppLinkResolving {
    get { defaultResolver }
    set { defaultResolver = newValue }
  }

  /**
   The default resolver to be used for App Link resolution. If the developer has not set one explicitly,
   a basic, built-in `WebViewAppLinkResolver` will be used.
   */
  @objc(defaultResolver)
  public static var defaultResolver: AppLinkResolving {
    get {
      // swiftformat:disable:next redundantSelf
      self.appLinkResolver ?? WebViewAppLinkResolver.shared
    }

    set {
      guard var dependencies = try? getDependencies() else {
        return
      }

      dependencies.appLinkResolver = newValue
      setDependencies(dependencies)
    }
  }

  /**
   The extras for the AppLinkNavigation. This will generally contain application-specific
   data that should be passed along with the request, such as advertiser or affiliate IDs or
   other such metadata relevant on this device.
   */
  public let extras: [String: Any]

  /**
   The al_applink_data for the AppLinkNavigation. This will generally contain data common to
   navigation attempts such as back-links, user agents, and other information that may be used
   in routing and handling an App Link request.
   */
  public let appLinkData: [String: Any]

  /// The AppLink to navigate to
  public let appLink: AppLink

  /**
   Returns navigation type for current instance. It does not produce any side-effects as the `navigate` method.
   */
  public var navigationType: AppLinkNavigationType {
    navigationType(for: appLink.targets)
  }

  /// Creates an AppLinkNavigation with the given link, extras, and App Link data
  @objc(initWithAppLink:extras:appLinkData:)
  public init(appLink: AppLink, extras: [String: Any], appLinkData: [String: Any]) {
    self.appLink = appLink
    self.extras = extras
    self.appLinkData = appLinkData
  }

  /// Creates an AppLinkNavigation with the given link, extras,  App Link data and settings
  @available(
    *,
    deprecated,
    message: """
      Please use init(appLink:extras:appLinkData:) to instantiate an `AppLinkNavigation`.
      This method will be removed in the next major version."
      """
  )
  @objc(initWithAppLink:extras:appLinkData:settings:)
  public convenience init(
    appLink: AppLink,
    extras: [String: Any],
    appLinkData: [String: Any],
    settings: SettingsProtocol
  ) {
    self.init(appLink: appLink, extras: extras, appLinkData: appLinkData)
  }

  /// Creates an AppLinkNavigation with the given link, extras, and App Link data. The `settings` argument will be ignored in favor of internal dependency injection.
  @available(
    *,
    deprecated,
    message: """
      Please use designated init to instantiate an AppLinkNavigation. This method will be removed in future releases."
      """
  )
  @objc(navigationWithAppLink:extras:appLinkData:settings:)
  public static func navigation(
    with appLink: AppLink,
    extras: [String: Any],
    appLinkData: [String: Any],
    settings: SettingsProtocol
  ) -> AppLinkNavigation {
    AppLinkNavigation(appLink: appLink, extras: extras, appLinkData: appLinkData)
  }

  /**
   Creates an instance of `[String: [String: String]]` with the correct format for iOS callback URLs to be used as 'appLinkData' argument in the call to init(appLink:extras:appLinkData:).
   */
  @objc(callbackAppLinkDataForAppWithName:url:)
  public static func callbackAppLinkData(forApp appName: String, url: String) -> [String: [String: String]] {
    [FBSDKAppLinkRefererAppLink: [FBSDKAppLinkRefererAppName: appName, FBSDKAppLinkRefererUrl: url]]
  }

  /// Performs the navigation
  @available(swift, obsoleted: 0.1)
  @objc(navigate:)
  public func navigate(error errorPointer: NSErrorPointer) -> AppLinkNavigationType {
    // This method can be removed when Objective-C support is removed.
    // The remaining navigation methods can then be refactored for a strict throw vs. return paradigm
    let (result, error) = navigate()
    errorPointer?.pointee = error as NSError?
    return result
  }

  /// Performs the navigation
  @nonobjc
  public func navigate() throws -> AppLinkNavigationType {
    let (result, error) = navigate()
    if let error = error {
      throw error
    } else {
      return result
    }
  }

  private func navigate() -> (AppLinkNavigationType, Error?) {
    let urlOpener: _InternalURLOpener
    do {
      urlOpener = try Self.getDependencies().urlOpener
    } catch {
      return (.failure, error)
    }

    var openedURL: URL?
    var navigationType: AppLinkNavigationType = .failure
    var lastError: Error?

    // Find the first eligible/launchable target in the AppLink.
    for target in appLink.targets {
      do {
        if let targetURL = target.url,
           let appLinkURL = try appLinkURL(targetURL: targetURL),
           urlOpener.open(appLinkURL) {
          openedURL = appLinkURL
          navigationType = .app
          break
        }
      } catch {
        lastError = error
      }
    }

    do {
      // Fall back to opening the url in the browser if available.
      if openedURL == nil,
         let webURL = appLink.webURL,
         let appLinkBrowserURL = try appLinkURL(targetURL: webURL),
         urlOpener.open(appLinkBrowserURL) {
        openedURL = appLinkBrowserURL
        navigationType = .browser
      }
    } catch {
      lastError = error
    }

    postNavigateEventNotification(targetURL: openedURL, error: lastError, navigationType: navigationType)
    return (navigationType, lastError)
  }

  /// Returns an AppLink for the given URL
  @objc(resolveAppLink:handler:)
  public static func resolveAppLink(_ destination: URL, handler: @escaping AppLinkBlock) {
    // swiftformat:disable:next redundantSelf
    guard let appLinkResolver = self.appLinkResolver else { return }

    resolveAppLink(destination, resolver: appLinkResolver, handler: handler)
  }

  /// Returns an AppLink for the given URL using the given App Link resolution strategy
  @objc(resolveAppLink:resolver:handler:)
  public static func resolveAppLink(_ destination: URL, resolver: AppLinkResolving, handler: @escaping AppLinkBlock) {
    resolver.appLink(from: destination, handler: handler)
  }

  /// Navigates to an AppLink and returns whether it opened in-app or in-browser
  @available(swift, obsoleted: 0.1)
  @objc(navigateToAppLink:error:)
  public static func navigate(to appLink: AppLink, errorPointer: ErrorPointer) -> AppLinkNavigationType {
    // This method can be removed when Objective-C support is removed.
    // The remaining navigation methods can then be refactored for a strict throw vs. return paradigm
    do {
      return try navigate(to: appLink)
    } catch {
      errorPointer?.pointee = error as NSError
      return .failure
    }
  }

  /// Navigates to an AppLink and returns whether it opened in-app or in-browser
  @nonobjc
  public static func navigate(to appLink: AppLink) throws -> AppLinkNavigationType {
    try AppLinkNavigation(appLink: appLink, extras: [:], appLinkData: [:]).navigate()
  }

  /**
   Returns an AppLinkNavigationType based on a FBSDKAppLink.
   It's essentially a no-side-effect version of navigateToAppLink:error:,
   allowing apps to determine flow based on the link type (e.g. open an
   internal web view instead of going straight to the browser for regular links.)
   */
  @objc(navigationTypeForLink:)
  public static func navigationType(for appLink: AppLink) -> AppLinkNavigationType {
    AppLinkNavigation(appLink: appLink, extras: [:], appLinkData: [:]).navigationType
  }

  /// Navigates to a URL (an asynchronous action) and returns a NavigationType
  @objc(navigateToURL:handler:)
  public static func navigate(to destination: URL, handler: @escaping AppLinkNavigationBlock) {
    // swiftformat:disable:next redundantSelf
    guard let appLinkResolver = self.appLinkResolver else { return }

    navigate(to: destination, resolver: appLinkResolver, handler: handler)
  }

  /**
   Navigates to a URL (an asynchronous action) using the given App Link resolution
   strategy and returns a NavigationType
   */
  @objc(navigateToURL:resolver:handler:)
  public static func navigate(
    to destination: URL,
    resolver: AppLinkResolving,
    handler: @escaping AppLinkNavigationBlock
  ) {
    DispatchQueue.main.async {
      resolveAppLink(destination, resolver: resolver) { appLink, error in
        guard
          let appLink = appLink,
          error == nil
        else {
          handler(.failure, error)
          return
        }

        do {
          let result = try navigate(to: appLink)
          handler(result, nil)
        } catch {
          handler(.failure, error)
        }
      }
    }
  }

  func appLinkURL(targetURL: URL) throws -> URL? {
    let settings = try Self.getDependencies().settings
    var appLinkData = appLinkData

    // Add applink protocol data
    if appLinkData[FBSDKAppLinkUserAgentKeyName] == nil {
      appLinkData[FBSDKAppLinkUserAgentKeyName] = "FBSDK \(settings.sdkVersion)"
    }

    if appLinkData[FBSDKAppLinkVersionKeyName] == nil {
      appLinkData[FBSDKAppLinkVersionKeyName] = AppLinkVersion
    }

    if let rawURL = appLink.sourceURL?.absoluteString {
      appLinkData[FBSDKAppLinkTargetKeyName] = rawURL
    }

    appLinkData[FBSDKAppLinkExtrasKeyName] = extras

    if JSONSerialization.isValidJSONObject(appLinkData) {
      // JSON-ify the applink data
      let jsonData = try JSONSerialization.data(withJSONObject: appLinkData)
      let jsonString = String(data: jsonData, encoding: .utf8)
      let encodedString = jsonString?.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
      let endURLString = """
        \(targetURL.absoluteString)\(targetURL.query != nil ? "&" : "?")\(FBSDKAppLinkDataParameterName)=\(encodedString)
        """
      return URL(string: endURLString)
    } else {
      throw NSError(domain: "JSON object not valid", code: -1)
    }
  }

  private enum LogDataKeys {
    static let outputURLScheme = "outputURLScheme"
    static let outputURL = "outputURL"
    static let sourceURL = "sourceURL"
    static let sourceHost = "sourceHost"
    static let sourceScheme = "sourceScheme"
    static let success = "success"
    static let type = "type"
    static let error = "error"
  }

  private enum EventValues {
    static let success = "1"
    static let failure = "0"

    static func getEventType(for navigationType: AppLinkNavigationType) -> (String?, String?) {
      switch navigationType {
      case .failure: return (failure, "fail")
      case .browser: return (success, "web")
      case .app: return (success, "app")
      @unknown default:
        return (nil, nil)
      }
    }
  }

  func postNavigateEventNotification(targetURL: URL?, error: Error?, navigationType: AppLinkNavigationType) {
    guard let appLinkEventPoster = Self.appLinkEventPoster else { return }

    var logData = [String: Any]()
    logData[LogDataKeys.outputURLScheme] = targetURL?.scheme
    logData[LogDataKeys.outputURL] = targetURL?.absoluteString
    logData[LogDataKeys.sourceURL] = appLink.sourceURL?.absoluteString
    logData[LogDataKeys.sourceHost] = appLink.sourceURL?.host
    logData[LogDataKeys.sourceScheme] = appLink.sourceURL?.scheme
    logData[LogDataKeys.error] = error?.localizedDescription

    let (result, type) = EventValues.getEventType(for: navigationType)
    logData[LogDataKeys.success] = result
    logData[LogDataKeys.type] = type

    if appLink.isBackToReferrer {
      appLinkEventPoster.postNotification(
        eventName: AppLinkNavigateBackToReferrerEventName,
        arguments: logData
      )
    } else {
      appLinkEventPoster.postNotification(
        eventName: AppLinkNavigateOutEventName,
        arguments: logData
      )
    }
  }

  func navigationType(for targets: [AppLinkTargetProtocol]) -> AppLinkNavigationType {
    guard let urlOpener = Self.urlOpener else { return .failure }

    let eligibleTarget = targets.first { target in
      guard let url = target.url else { return false }

      return urlOpener.canOpen(url)
    }

    if let eligibleTargetURL = eligibleTarget?.url,
       (try? appLinkURL(targetURL: eligibleTargetURL)) != nil {
      return .app
    } else if let webURL = appLink.webURL,
              (try? appLinkURL(targetURL: webURL)) != nil {
      return .browser
    } else {
      return .failure
    }
  }
}

extension AppLinkNavigation: DependentAsType {
  struct TypeDependencies {
    var settings: SettingsProtocol
    var urlOpener: _InternalURLOpener
    var appLinkEventPoster: _AppLinkEventPosting
    var appLinkResolver: AppLinkResolving
  }

  static var configuredDependencies: TypeDependencies?
  static var defaultDependencies: TypeDependencies?
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

/**
 Represents a target defined in App Link metadata, consisting of at least
 a URL, and optionally an App Store ID and name.
 */
@objcMembers
@objc(FBSDKAppLinkTarget)
public final class AppLinkTarget: NSObject, AppLinkTargetProtocol {

  /// The URL prefix for this app link target
  public let url: URL?

  /// The app ID for the app store
  public let appStoreId: String?

  /// The name of the app
  public let appName: String

  /// Creates a AppLinkTarget with the given app site and target URL.
  @objc(initWithURL:appStoreId:appName:)
  public init(url: URL?, appStoreId: String?, appName: String) {
    self.url = url
    self.appStoreId = appStoreId
    self.appName = appName
  }

  /// Creates a AppLinkTarget with the given app site and target URL.
  @available(
    *,
    deprecated,
    message: """
      Please use designated init to instantiate an AppLinkTarget. This method will be removed in future releases."
      """
  )
  @objc(appLinkTargetWithURL:appStoreId:appName:)
  public static func appLinkTargetWithURL(url: URL?, appStoreId: String?, appName: String) -> AppLinkTarget {
    AppLinkTarget(url: url, appStoreId: appStoreId, appName: appName)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

final class AppLinkURLFactory: NSObject, _AppLinkURLCreating {
  func createAppLinkURL(with url: URL) -> _AppLinkURLProtocol {
    AppLinkURL(url: url)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

/**
 Provides an implementation of the AppLinkResolving protocol that uses the Facebook App Link
 Index API to resolve App Links given a URL. It also provides an additional helper method that can resolve
 multiple App Links in a single call.
 */
@objcMembers
@objc(FBSDKAppLinkResolver)
public final class AppLinkResolver: NSObject, AppLinkResolving {
  var cachedAppLinks = NSMutableDictionary()

  private enum Keys {
    static let url = "url"
    static let iosAppStoreId = "app_store_id"
    static let iosAppName = "app_name"
    static let web = "web"
    static let ios = "ios"
    static let iphone = "iphone"
    static let ipad = "ipad"
    static let shouldFallBack = "should_fallback"
    static let appLinks = "app_links"
  }

  public func appLink(from url: URL, handler: @escaping AppLinkBlock) {
    appLinks(from: [url]) { urls, error in
      handler(urls[url], error)
    }
  }

  /**
   Asynchronously resolves App Link data for a given array of URLs.

   @param urls The URLs to resolve into an App Link.
   @param handler The completion block that will return an App Link for the given URL.
   */
  @available(iOSApplicationExtension, unavailable, message: "Not available in app extension")
  public func appLinks(from urls: [URL], handler: @escaping AppLinksBlock) {
    guard let dependencies = try? Self.getDependencies() else {
      handler([:], nil)
      return
    }

    if dependencies.clientTokenProvider.clientToken == nil,
       dependencies.accessTokenProvider.current == nil {
      _Logger.singleShotLogEntry(
        .developerErrors,
        logEntry: "A user access token or clientToken is required to use AppLinkResolver"
      )
    }

    var appLinks = [URL: AppLink]()
    var appLinkURLs = [URL]()

    synchronized(cachedAppLinks) {
      for url in urls {
        if let cachedAppLink = cachedAppLinks[url] as? AppLink {
          appLinks[url] = cachedAppLink
        } else {
          appLinkURLs.append(url)
        }
      }
    }

    guard !appLinkURLs.isEmpty else {
      // All of the URLs have already been found.
      handler(appLinks, nil)
      return
    }

    let request = dependencies.requestBuilder.request(for: urls)

    request.start { _, response, error in

      guard
        error == nil,
        let response = response as? [String: [String: Any]]
      else {
        handler([:], error)
        return
      }

      for url in appLinkURLs {
        let link = self.buildAppLink(for: url, result: response)

        synchronized(self.cachedAppLinks) {
          self.cachedAppLinks[url] = link
        }

        appLinks[url] = link
      }

      handler(appLinks, nil)
    }
  }

  func buildAppLink(for url: URL, result: [String: [String: Any]]) -> AppLink {
    guard let requestBuilder = try? Self.getDependencies().requestBuilder else {
      fatalError("Dependencies have not been set")
    }

    let nestedObject = result[url.absoluteString]?[Keys.appLinks] as? [String: Any] ?? [:]
    var rawTargets = [[String: String]]()

    if let idiomSpecificField = requestBuilder.getIdiomSpecificField(),
       let idiomSpecificFields = nestedObject[idiomSpecificField] as? [[String: String]] {
      rawTargets.append(contentsOf: idiomSpecificFields)
    }

    if let iOSFields = nestedObject[Keys.ios] as? [[String: String]] {
      rawTargets.append(contentsOf: iOSFields)
    }

    var targets = [AppLinkTarget]()

    for rawTarget in rawTargets {
      let urlString = rawTarget[Keys.url]
      let url = URL(string: urlString ?? "")
      let appStoreID = rawTarget[Keys.iosAppStoreId]
      let appName = rawTarget[Keys.iosAppName]
      let appLinkTarget = AppLinkTarget(url: url, appStoreId: appStoreID, appName: appName ?? "")
      targets.append(appLinkTarget)
    }

    let webTarget = nestedObject[Keys.web] as? [String: Any] ?? [:]
    let webFallbackString = webTarget[Keys.url] as? String ?? ""
    var fallbackURL: URL? = URL(string: webFallbackString) ?? url

    if let shouldFallback = webTarget[Keys.shouldFallBack] as? Bool,
       !shouldFallback {
      fallbackURL = nil
    }

    return AppLink(sourceURL: url, targets: targets, webURL: fallbackURL)
  }
}

extension AppLinkResolver: DependentAsType {
  struct TypeDependencies {
    var requestBuilder: _AppLinkResolverRequestBuilding
    var clientTokenProvider: _ClientTokenProviding
    var accessTokenProvider: _AccessTokenProviding.Type
  }

  static var configuredDependencies: TypeDependencies?

  static var defaultDependencies: TypeDependencies? = TypeDependencies(
    requestBuilder: AppLinkResolverRequestBuilder(),
    clientTokenProvider: Settings.shared,
    accessTokenProvider: AccessToken.self
  )
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

/**
 Class responsible for generating the appropriate GraphRequest for a given set of urls
 */
final class AppLinkResolverRequestBuilder: NSObject, _AppLinkResolverRequestBuilding {

  private enum IdiomField: String {
    case ios
    case iphone
    case ipad
    case appLinks = "app_links"
  }

  private let userInterfaceIdiom: UIUserInterfaceIdiom
  private var uiSpecificFields: [String] {
    var fields = [IdiomField.ios.rawValue]

    if let idiomSpecificField = getIdiomSpecificField() {
      fields.append(idiomSpecificField)
    }

    return fields
  }

  init(userInterfaceIdiom: UIUserInterfaceIdiom = UIDevice.current.userInterfaceIdiom) {
    self.userInterfaceIdiom = userInterfaceIdiom
  }

  func request(for urls: [URL]) -> GraphRequestProtocol {
    let fields = uiSpecificFields.joined(separator: ",")
    let encodedURLs = getEncodedURLs(urls).joined(separator: ",")

    let path = "?fields=\(IdiomField.appLinks.rawValue).fields(\(fields))&ids=\(encodedURLs)"
    return GraphRequest(
      graphPath: path,
      parameters: nil,
      flags: [.doNotInvalidateTokenOnError, .disableErrorRecovery]
    )
  }

  func getIdiomSpecificField() -> String? {
    switch userInterfaceIdiom {
    case .pad: return IdiomField.ipad.rawValue
    case .phone: return IdiomField.iphone.rawValue
    default: return nil
    }
  }

  private func getEncodedURLs(_ urls: [URL]) -> [String] {
    urls.compactMap {
      $0.absoluteString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
    }
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

final class AppLinkFactory: _AppLinkCreating {
  func createAppLink(
    sourceURL: URL?,
    targets: [AppLinkTargetProtocol],
    webURL: URL?,
    isBackToReferrer: Bool
  ) -> _AppLinkProtocol {
    AppLink(
      sourceURL: sourceURL,
      targets: targets,
      webURL: webURL,
      isBackToReferrer: isBackToReferrer
    )
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

final class AppLinkTargetFactory: _AppLinkTargetCreating {
  func createAppLinkTarget(url: URL?, appStoreId: String?, appName: String) -> AppLinkTargetProtocol {
    AppLinkTarget(url: url, appStoreId: appStoreId, appName: appName)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

struct BackgroundEventLogger: BackgroundEventLogging {

  private enum Keys {
    static let schedulerIdentifiers = "BGTaskSchedulerPermittedIdentifiers"
  }

  var isNewBackgroundRefresh: Bool {
    let infoDictionaryProvider = Self.infoDictionaryProvider
    return infoDictionaryProvider?.fb_object(forInfoDictionaryKey: Keys.schedulerIdentifiers) != nil
  }

  func logBackgroundRefreshStatus(_ status: UIBackgroundRefreshStatus) {
    let eventLogger = Self.eventLogger

    switch status {
    case .available:
      eventLogger?.logInternalEvent(
        .backgroundStatusAvailable,
        parameters: [.version: isNewBackgroundRefresh],
        isImplicitlyLogged: true
      )
    case .denied:
      eventLogger?.logInternalEvent(.backgroundStatusDenied, isImplicitlyLogged: true)
    case .restricted:
      eventLogger?.logInternalEvent(.backgroundStatusRestricted, isImplicitlyLogged: true)
    @unknown default:
      break
    }
  }
}

extension BackgroundEventLogger: DependentAsType {
  struct TypeDependencies {
    var infoDictionaryProvider: InfoDictionaryProviding
    var eventLogger: EventLogging
  }

  static var configuredDependencies: TypeDependencies?

  static var defaultDependencies: TypeDependencies? = TypeDependencies(
    infoDictionaryProvider: Bundle.main,
    eventLogger: AppEvents.shared
  )
}

extension AppEvents.ParameterName {
  fileprivate static let version = Self("version")
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

protocol BackgroundEventLogging {
  func logBackgroundRefreshStatus(_ status: UIBackgroundRefreshStatus)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

/**
 Internal type exposed to facilitate transition to Swift.
 API Subject to change or removal without warning. Do not use.

 @warning INTERNAL - DO NOT USE
 */
@objcMembers
@objc public final class CoreUIApplication: NSObject {
  public static let shared: _InternalURLOpener = {
    #if DEBUG
    TestUIApplication()
    #else
    UIApplication.shared
    #endif
  }()
}

#if DEBUG
private final class TestUIApplication: _InternalURLOpener {
  func canOpen(_ url: URL) -> Bool { false }

  func open(_ url: URL) -> Bool { false }

  func open(
    _ url: URL,
    options: [UIApplication.OpenExternalURLOptionsKey: Any] = [:],
    completionHandler completion: ((Bool) -> Void)? = nil
  ) {}
}
#endif


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

struct ProfilePictureViewState: Equatable {
  let profileID: String
  let size: CGSize
  let scale: CGFloat
  let pictureMode: Profile.PictureMode
  let imageShouldFit: Bool
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

/**
 Internal type exposed to facilitate transition to Swift.
 API Subject to change or removal without warning. Do not use.

 @warning INTERNAL - DO NOT USE
 */
@objc(FBSDKErrorFactory)
public final class _ErrorFactory: NSObject, ErrorCreating {

  private enum Keys {
    static let errorDomain = "com.facebook.sdk.core"
    static let message = "com.facebook.sdk:FBSDKErrorDeveloperMessageKey"
    static let argumentName = "com.facebook.sdk:FBSDKErrorArgumentNameKey"
    static let argumentValue = "com.facebook.sdk:FBSDKErrorArgumentValueKey"
  }

  // MARK: - General Errors

  @objc(errorWithCode:userInfo:message:underlyingError:)
  public func error(
    code: Int,
    userInfo: [String: Any]? = nil,
    message: String?,
    underlyingError: Error?
  ) -> Error {
    error(
      domain: Keys.errorDomain,
      code: code,
      userInfo: userInfo,
      message: message,
      underlyingError: underlyingError
    )
  }

  @objc(errorWithDomain:code:userInfo:message:underlyingError:)
  public func error(
    domain: String,
    code: Int,
    userInfo: [String: Any]? = nil,
    message: String?,
    underlyingError: Error?
  ) -> Error {

    var fullUserInfo = userInfo ?? [:]
    if let message = message {
      fullUserInfo[Keys.message] = message
    }

    if let underlyingError = underlyingError {
      fullUserInfo[NSUnderlyingErrorKey] = underlyingError
    }

    reportError(code: code, domain: domain, message: message)
    return NSError(domain: domain, code: code, userInfo: fullUserInfo)
  }

  // MARK: - Invalid Argument Errors

  @objc(invalidArgumentErrorWithName:value:message:underlyingError:)
  public func invalidArgumentError(
    name: String,
    value: Any?,
    message: String?,
    underlyingError: Error?
  ) -> Error {
    invalidArgumentError(
      domain: Keys.errorDomain,
      name: name,
      value: value,
      message: message,
      underlyingError: underlyingError
    )
  }

  @objc(invalidArgumentErrorWithDomain:name:value:message:underlyingError:)
  public func invalidArgumentError(
    domain: String,
    name: String,
    value: Any?,
    message: String?,
    underlyingError: Error?
  ) -> Error {
    let message = message ?? "Invalid value for \(name): \(String(describing: value))"
    var userInfo = [String: Any]()
    userInfo[Keys.argumentName] = name

    if let value = value {
      userInfo[Keys.argumentValue] = value
    }

    return error(
      domain: domain,
      code: CoreError.errorInvalidArgument.rawValue,
      userInfo: userInfo,
      message: message,
      underlyingError: underlyingError
    )
  }

  // MARK: - Required Argument Errors

  @objc(requiredArgumentErrorWithName:message:underlyingError:)
  public func requiredArgumentError(name: String, message: String?, underlyingError: Error?) -> Error {
    requiredArgumentError(domain: Keys.errorDomain, name: name, message: message, underlyingError: underlyingError)
  }

  @objc(requiredArgumentErrorWithDomain:name:message:underlyingError:)
  public func requiredArgumentError(
    domain: String,
    name: String,
    message: String?,
    underlyingError: Error?
  ) -> Error {
    let message = message ?? "Value for \(name) is required."
    return invalidArgumentError(
      domain: domain,
      name: name,
      value: nil,
      message: message,
      underlyingError: underlyingError
    )
  }

  @objc(unknownErrorWithMessage:userInfo:)
  public func unknownError(message: String?, userInfo: [String: Any]? = nil) -> Error {
    error(code: CoreError.errorUnknown.rawValue, userInfo: userInfo, message: message, underlyingError: nil)
  }

  // MARK: - Reporting

  func reportError(code: Int, domain: String, message: String?) {
    guard
      let reporter = try? Self.getDependencies().reporter
    else {
      return
    }
    reporter.saveError(code, errorDomain: domain, message: message)
  }
}

// MARK: - Dependencies

extension _ErrorFactory: DependentAsType {
  struct TypeDependencies {
    var reporter: ErrorReporting
  }

  static var configuredDependencies: TypeDependencies?

  static var defaultDependencies: TypeDependencies? = TypeDependencies(reporter: ErrorReporter.shared)
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

protocol BridgeAPIResponseCreating {
  func createResponse(request: BridgeAPIRequestProtocol, error: Error) -> BridgeAPIResponse

  func createResponse(
    request: BridgeAPIRequestProtocol,
    responseURL: URL,
    sourceApplication: String?
  ) throws -> BridgeAPIResponse

  func createResponseCancelled(request: BridgeAPIRequestProtocol) -> BridgeAPIResponse
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Foundation

/**
 Internal Type exposed to facilitate transition to Swift.
 API Subject to change or removal without warning. Do not use.

 @warning INTERNAL - DO NOT USE
 */
@objcMembers
@objc(FBSDKBridgeAPIRequestFactory)
public final class _BridgeAPIRequestFactory: NSObject, BridgeAPIRequestCreating {
  public func bridgeAPIRequest(
    with protocolType: FBSDKBridgeAPIProtocolType,
    scheme: String,
    methodName: String?,
    parameters: [String: Any]?,
    userInfo: [String: Any]? = nil
  ) -> BridgeAPIRequestProtocol? {
    _BridgeAPIRequest(
      protocolType: protocolType,
      scheme: URLScheme(rawValue: scheme),
      methodName: methodName,
      parameters: parameters,
      userInfo: userInfo
    )
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

final class _BridgeAPIResponseFactory: NSObject, BridgeAPIResponseCreating {
  func createResponse(request: BridgeAPIRequestProtocol, error: Error) -> BridgeAPIResponse {
    BridgeAPIResponse(request: request, error: error)
  }

  func createResponse(
    request: BridgeAPIRequestProtocol,
    responseURL: URL,
    sourceApplication: String?
  ) throws -> BridgeAPIResponse {
    try BridgeAPIResponse(
      request: request,
      responseURL: responseURL,
      sourceApplication: sourceApplication
    )
  }

  func createResponseCancelled(request: BridgeAPIRequestProtocol) -> BridgeAPIResponse {
    BridgeAPIResponse(cancelledWith: request)
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import UIKit

/**
 Internal Type exposed to facilitate transition to Swift.
 API Subject to change or removal without warning. Do not use.

 @warning INTERNAL - DO NOT USE
 */
@objcMembers
@objc(FBSDKBridgeAPIProtocolNativeV1)
public final class _BridgeAPIProtocolNativeV1: NSObject, BridgeAPIProtocol {
  let appScheme: String?
  let dataLengthThreshold: UInt
  let shouldIncludeAppIcon: Bool
  let pasteboard: _Pasteboard?
  static let defaultMaxBase64DataLengthThreshold: UInt = 1024 * 16
  private var observer: Any?

  var appIcon: UIImage? {
    guard
      shouldIncludeAppIcon,
      let bundle = try? Self.getDependencies().bundle,
      let icons = bundle.fb_object(forInfoDictionaryKey: InfoDictionaryKeys.icons) as? [String: [String: Any]],
      let primaryIcons = icons[InfoDictionaryKeys.primaryIcon],
      let iconNames = primaryIcons[InfoDictionaryKeys.iconFiles] as? [String],
      let iconName = iconNames.first
    else {
      return nil
    }

    return UIImage(named: iconName)
  }

  private enum DataTypeTags {
    static let data = "data"

    // we serialize jpegs but use png for backward compatibility - it is any image format that UIImage can handle
    static let image = "png"
  }

  private enum PasteboardKeys {
    static let isBase64 = "isBase64"
    static let isPasteboard = "isPasteboard"
    static let tag = "tag"
    static let value = "fbAppBridgeType_jsonReadyValue"
    static let pasteboard = "com.facebook.Facebook.FBAppBridgeType"
  }

  private enum InfoDictionaryKeys {
    static let icons = "CFBundleIcons"
    static let primaryIcon = "CFBundlePrimaryIcon"
    static let iconFiles = "CFBundleIconFiles"
  }

  private enum InputKeys {
    static let error = "error"
    static let methodResults = "method_results"
    static let completionGesture = "completionGesture"
    static let cancel = "cancel"
    static let dialog = "dialog"
  }

  private enum BridgeParameterOutputKeys {
    static let actionID = "action_id"
    static let appIcon = "app_icon"
    static let appName = "app_name"
    static let sdkVersion = "sdk_version"
  }

  private enum OutputKeys {
    static let bridge = "bridge_args"
    static let method = "method_args"
  }

  private enum ErrorKeys {
    static let code = "code"
    static let domain = "domain"
    static let userInfo = "user_info"
  }

  @objc(initWithAppScheme:)
  public convenience init(appScheme: String?) {
    self.init(
      appScheme: appScheme,
      pasteboard: UIPasteboard.general,
      dataLengthThreshold: Self.defaultMaxBase64DataLengthThreshold,
      shouldIncludeAppIcon: true
    )
  }

  @objc(initWithAppScheme:pasteboard:dataLengthThreshold:includeAppIcon:)
  public init(appScheme: String?, pasteboard: _Pasteboard?, dataLengthThreshold: UInt, shouldIncludeAppIcon: Bool) {
    self.appScheme = appScheme
    self.pasteboard = pasteboard
    self.dataLengthThreshold = dataLengthThreshold
    self.shouldIncludeAppIcon = shouldIncludeAppIcon
  }

  deinit {
    Self.notificationDeliverer?.fb_removeObserver(observer as Any)
  }

  public func requestURL(
    actionID: String,
    scheme: String,
    methodName: String,
    parameters: [String: Any]
  ) throws -> URL {

    var queryParameters = [String: String]()

    if !parameters.isEmpty {
      let parameterString = try getJSON(dictionary: parameters, enabledPasteboard: true)

      let escapedParameterString = parameterString.replacingOccurrences(
        of: "&",
        with: "%26",
        options: .caseInsensitive
      )
      queryParameters[OutputKeys.method] = escapedParameterString
    }

    let bridgeParameters = getBridgeParameters(actionID: actionID)
    let bridgeParametersString = try getJSON(dictionary: bridgeParameters, enabledPasteboard: false)

    queryParameters[OutputKeys.bridge] = bridgeParametersString

    let internalUtility = try Self.getDependencies().internalUtility
    return try internalUtility.url(
      withScheme: appScheme ?? "",
      host: InputKeys.dialog,
      path: "/\(methodName)",
      queryParameters: queryParameters
    )
  }

  public func responseParameters(
    actionID: String,
    queryParameters: [String: Any],
    cancelled cancelledRef: UnsafeMutablePointer<ObjCBool>?
  ) throws -> [String: Any] {

    if let cancelled = cancelledRef {
      cancelled.pointee = false
    }

    let errorFactory = try Self.getDependencies().errorFactory

    let bridgeParametersJSON = queryParameters[OutputKeys.bridge] as? String ?? ""
    var bridgeParameters = [String: Any]()

    do {
      guard
        let parameters = try BasicUtility.object(forJSONString: bridgeParametersJSON) as? [String: Any],
        let responseActionID = parameters[BridgeParameterOutputKeys.actionID] as? String,
        responseActionID == actionID
      else {
        return [:]
      }
      bridgeParameters = parameters
    } catch {
      throw errorFactory.invalidArgumentError(
        name: OutputKeys.bridge,
        value: bridgeParametersJSON,
        message: "Invalid bridge_args.",
        underlyingError: error
      )
    }

    if let errorDictionary = bridgeParameters[InputKeys.error] as? [String: Any] {
      throw try getError(dictionary: errorDictionary)
    }

    let resultParametersJSON = queryParameters[InputKeys.methodResults] as? String ?? ""

    do {
      guard
        let resultParameters = try BasicUtility.object(forJSONString: resultParametersJSON) as? [String: Any]
      else {
        return [:]
      }

      if let cancelled = cancelledRef,
         let completionGesture = resultParameters[InputKeys.completionGesture] as? String {
        cancelled.pointee = ObjCBool(completionGesture == InputKeys.cancel)
      }

      return resultParameters
    } catch {
      throw errorFactory.invalidArgumentError(
        name: InputKeys.methodResults,
        value: resultParametersJSON,
        message: "Invalid method_results.",
        underlyingError: error
      )
    }
  }

  private func getBridgeParameters(actionID: String) -> [String: Any] {
    [
      BridgeParameterOutputKeys.actionID: actionID,
      BridgeParameterOutputKeys.appIcon: appIcon as Any,
      BridgeParameterOutputKeys.appName: Settings.shared.displayName as Any,
      BridgeParameterOutputKeys.sdkVersion: Settings.shared.sdkVersion,
    ]
  }

  private func getError(dictionary: [String: Any]) throws -> Error {
    let errorFactory = try Self.getDependencies().errorFactory

    let domain = dictionary[ErrorKeys.domain] as? String ?? ErrorDomain
    let code = dictionary[ErrorKeys.code] as? Int ?? CoreError.errorUnknown.rawValue
    let userInfo = dictionary[ErrorKeys.userInfo] as? [String: Any]
    return errorFactory.error(domain: domain, code: code, userInfo: userInfo, message: nil, underlyingError: nil)
  }

  private func getJSON(dictionary: [String: Any], enabledPasteboard: Bool) throws -> String {
    var didAddToPasteboard = false
    return try BasicUtility.jsonString(for: dictionary) { [self] invalidObject, _ in
      var dataTag = DataTypeTags.data
      var invalidObject = invalidObject

      if let image = invalidObject as? UIImage {
        // due to backward compatibility, we must send UIImage as Data even though UIPasteboard can handle UIImage
        invalidObject = image.jpegData(compressionQuality: Settings.shared.jpegCompressionQuality) as Any
        dataTag = DataTypeTags.image
      }

      if let data = invalidObject as? Data {
        var dictionary = [String: Any]()
        if didAddToPasteboard || !enabledPasteboard || pasteboard == nil || data.count < dataLengthThreshold {
          dictionary[PasteboardKeys.isBase64] = true
          dictionary[PasteboardKeys.tag] = dataTag
          dictionary[PasteboardKeys.value] = data.base64EncodedString(options: [])
        } else {
          dictionary[PasteboardKeys.isPasteboard] = true
          dictionary[PasteboardKeys.tag] = dataTag
          dictionary[PasteboardKeys.value] = pasteboard?.name
          pasteboard?.setData(data, forPasteboardType: PasteboardKeys.pasteboard)

          // this version of the protocol only supports a single item on the pasteboard, so if when we add an item, make
          // sure we don't add another item
          didAddToPasteboard = true

          // if we are adding this to the general pasteboard, then we want to remove it when we are done with the share.
          // the Facebook app will not clear the value with this version of the protocol, so we should do it when the app
          // becomes active again
          if pasteboard?._isGeneralPasteboard ?? false {
            clearPasteboardDataOnActivation(matching: data)
          }
        }
        return dictionary
      } else if let url = invalidObject as? URL {
        return url.absoluteString
      }

      return invalidObject
    }
  }

  private func clearPasteboardDataOnActivation(matching data: Data) {
    guard
      let notificationDeliverer = try? Self.getDependencies().notificationDeliverer
    else {
      return
    }

    observer = notificationDeliverer.fb_addObserver(
      forName: .FBSDKApplicationDidBecomeActive,
      object: nil,
      queue: nil
    ) { _ in
      let pasteboardData = self.pasteboard?.data(forPasteboardType: PasteboardKeys.pasteboard)
      if data == pasteboardData {
        self.pasteboard?.setData(Data(), forPasteboardType: PasteboardKeys.pasteboard)
      }
    }
  }
}

extension _BridgeAPIProtocolNativeV1: DependentAsType {
  struct TypeDependencies {
    var errorFactory: ErrorCreating
    var bundle: InfoDictionaryProviding
    var notificationDeliverer: NotificationDelivering
    var internalUtility: InternalUtilityProtocol
  }

  static var configuredDependencies: TypeDependencies?

  static var defaultDependencies: TypeDependencies? = TypeDependencies(
    errorFactory: _ErrorFactory(),
    bundle: Bundle.main,
    notificationDeliverer: NotificationCenter.default,
    internalUtility: InternalUtility.shared
  )
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import FBAEMKit

final class CoreKitComponents {
  let accessTokenExpirer: _AccessTokenExpiring
  let accessTokenWallet: (_AccessTokenProviding & _TokenStringProviding).Type
  let advertiserIDProvider: _AdvertiserIDProviding
  let aemNetworker: AEMNetworking?
  let aemReporter: _AEMReporterProtocol.Type
  let appEventParametersExtractor: _AppEventParametersExtracting
  let appEvents: _SourceApplicationTracking & _AppEventsConfiguring & _ApplicationLifecycleObserving
    & _ApplicationActivating & _ApplicationStateSetting & EventLogging
  let appEventsConfigurationProvider: _AppEventsConfigurationProviding
  let appEventsDropDeterminer: _AppEventDropDetermining
  let appEventsStateProvider: _AppEventsStateProviding
  let appEventsStateStore: _AppEventsStatePersisting
  let appEventsUtility: _AppEventDropDetermining & _AppEventParametersExtracting
    & _AppEventsUtilityProtocol & _LoggingNotifying
  let appLinkEventPoster: _AppLinkEventPosting
  let appLinkFactory: _AppLinkCreating
  let appLinkResolver: AppLinkResolving
  let appLinkTargetFactory: _AppLinkTargetCreating
  let appLinkURLFactory: _AppLinkURLCreating
  let atePublisherFactory: _ATEPublisherCreating
  let authenticationTokenWallet: _AuthenticationTokenProviding.Type
  let backgroundEventLogger: BackgroundEventLogging
  let capiReporter: CAPIReporter
  let codelessIndexer: _CodelessIndexing.Type
  let crashHandler: CrashHandlerProtocol
  let crashObserver: CrashObserving
  let dataExtractor: _FileDataExtracting.Type
  let defaultDataStore: DataPersisting
  let deviceInformationProvider: _DeviceInformationProviding
  let dialogConfigurationMapBuilder: _DialogConfigurationMapBuilding
  let errorConfigurationProvider: _ErrorConfigurationProviding
  let errorFactory: ErrorCreating
  let errorReporter: ErrorReporting
  let eventDeactivationManager: _AppEventsParameterProcessing & _EventsProcessing
  let eventLogger: EventLogging
  let featureChecker: FeatureChecking & _FeatureDisabling
  let featureExtractor: _FeatureExtracting.Type
  let fileManager: _FileManaging
  let gateKeeperManager: _GateKeeperManaging.Type
  let getApplicationActivationNotifier: () -> Any
  let graphRequestConnectionFactory: GraphRequestConnectionFactoryProtocol
  let graphRequestFactory: GraphRequestFactoryProtocol
  let impressionLoggerFactory: _ImpressionLoggerFactoryProtocol
  let infoDictionaryProvider: InfoDictionaryProviding
  let internalURLOpener: _InternalURLOpener
  let internalUtility: InternalUtilityProtocol
  let logger: Logging.Type
  let loggerFactory: _LoggerCreating
  let macCatalystDeterminator: _MacCatalystDetermining
  let metadataIndexer: _MetadataIndexing
  let modelManager: _EventProcessing & _IntegrityParametersProcessorProvider
  let notificationCenter: _NotificationPosting & NotificationDelivering
  let operatingSystemVersionComparer: _OperatingSystemVersionComparing
  let paymentObserver: _PaymentObserving
  let piggybackManager: _GraphRequestPiggybackManaging
  let profileSetter: ProfileProviding.Type
  let restrictiveDataFilterManager: _AppEventsParameterProcessing & _EventsProcessing
  let rulesFromKeyProvider: _RulesFromKeyProvider
  let serverConfigurationProvider: _ServerConfigurationProviding
  let sessionDataTaskProvider: URLSessionProviding
  let settings: SettingsProtocol & SettingsLogging
  let skAdNetworkReporter: (_AppEventsReporter & SKAdNetworkReporting)?
  let suggestedEventsIndexer: _SuggestedEventsIndexerProtocol
  let swizzler: _Swizzling.Type
  let timeSpentRecorder: _SourceApplicationTracking & _TimeSpentRecording
  let tokenCache: TokenCaching
  let urlHoster: URLHosting
  let urlSessionProxyFactory: _URLSessionProxyProviding
  let userDataStore: _UserDataPersisting
  let userIDProvider: _UserIDProviding
  let webViewProvider: _WebViewProviding
  let aemManager: _AutoSetup
  let protectedModeManager: _AppEventsParameterProcessing
  let macaRuleMatchingManager: MACARuleMatching

  // MARK: - Initializers

  init(
    accessTokenExpirer: _AccessTokenExpiring,
    accessTokenWallet: (_AccessTokenProviding & _TokenStringProviding).Type,
    advertiserIDProvider: _AdvertiserIDProviding,
    aemNetworker: AEMNetworking?,
    aemReporter: _AEMReporterProtocol.Type,
    appEventParametersExtractor: _AppEventParametersExtracting,
    appEvents: EventLogging & _AppEventsConfiguring & _ApplicationActivating & _ApplicationLifecycleObserving & _ApplicationStateSetting & _SourceApplicationTracking, // swiftlint:disable:this line_length
    appEventsConfigurationProvider: _AppEventsConfigurationProviding,
    appEventsDropDeterminer: _AppEventDropDetermining,
    appEventsStateProvider: _AppEventsStateProviding,
    appEventsStateStore: _AppEventsStatePersisting,
    appEventsUtility: _AppEventDropDetermining & _AppEventParametersExtracting & _AppEventsUtilityProtocol & _LoggingNotifying, // swiftlint:disable:this line_length
    appLinkEventPoster: _AppLinkEventPosting,
    appLinkFactory: _AppLinkCreating,
    appLinkResolver: AppLinkResolving,
    appLinkTargetFactory: _AppLinkTargetCreating,
    appLinkURLFactory: _AppLinkURLCreating,
    atePublisherFactory: _ATEPublisherCreating,
    authenticationTokenWallet: _AuthenticationTokenProviding.Type,
    backgroundEventLogger: BackgroundEventLogging,
    capiReporter: CAPIReporter,
    codelessIndexer: _CodelessIndexing.Type,
    crashHandler: CrashHandlerProtocol,
    crashObserver: CrashObserving,
    dataExtractor: _FileDataExtracting.Type,
    defaultDataStore: DataPersisting,
    deviceInformationProvider: _DeviceInformationProviding,
    dialogConfigurationMapBuilder: _DialogConfigurationMapBuilding,
    errorConfigurationProvider: _ErrorConfigurationProviding,
    errorFactory: ErrorCreating,
    errorReporter: ErrorReporting,
    eventDeactivationManager: _AppEventsParameterProcessing & _EventsProcessing,
    eventLogger: EventLogging,
    featureChecker: FeatureChecking & _FeatureDisabling,
    featureExtractor: _FeatureExtracting.Type,
    fileManager: _FileManaging,
    gateKeeperManager: _GateKeeperManaging.Type,
    getApplicationActivationNotifier: @escaping () -> Any,
    graphRequestConnectionFactory: GraphRequestConnectionFactoryProtocol,
    graphRequestFactory: GraphRequestFactoryProtocol,
    impressionLoggerFactory: _ImpressionLoggerFactoryProtocol,
    infoDictionaryProvider: InfoDictionaryProviding,
    internalURLOpener: _InternalURLOpener,
    internalUtility: InternalUtilityProtocol,
    logger: Logging.Type,
    loggerFactory: _LoggerCreating,
    macCatalystDeterminator: _MacCatalystDetermining,
    metadataIndexer: _MetadataIndexing,
    modelManager: _EventProcessing & _IntegrityParametersProcessorProvider,
    notificationCenter: _NotificationPosting & NotificationDelivering,
    operatingSystemVersionComparer: _OperatingSystemVersionComparing,
    paymentObserver: _PaymentObserving,
    piggybackManager: _GraphRequestPiggybackManaging,
    profileSetter: ProfileProviding.Type,
    restrictiveDataFilterManager: _AppEventsParameterProcessing & _EventsProcessing,
    rulesFromKeyProvider: _RulesFromKeyProvider,
    serverConfigurationProvider: _ServerConfigurationProviding,
    sessionDataTaskProvider: URLSessionProviding,
    settings: SettingsLogging & SettingsProtocol,
    skAdNetworkReporter: (SKAdNetworkReporting & _AppEventsReporter)?,
    suggestedEventsIndexer: _SuggestedEventsIndexerProtocol,
    swizzler: _Swizzling.Type,
    timeSpentRecorder: _SourceApplicationTracking & _TimeSpentRecording,
    tokenCache: TokenCaching,
    urlHoster: URLHosting,
    urlSessionProxyFactory: _URLSessionProxyProviding,
    userDataStore: _UserDataPersisting,
    userIDProvider: _UserIDProviding,
    webViewProvider: _WebViewProviding,
    aemManager: _AutoSetup,
    protectedModeManager: _AppEventsParameterProcessing,
    macaRuleMatchingManager: MACARuleMatching
  ) {
    self.accessTokenExpirer = accessTokenExpirer
    self.accessTokenWallet = accessTokenWallet
    self.advertiserIDProvider = advertiserIDProvider
    self.aemNetworker = aemNetworker
    self.aemReporter = aemReporter
    self.appEventParametersExtractor = appEventParametersExtractor
    self.appEvents = appEvents
    self.appEventsConfigurationProvider = appEventsConfigurationProvider
    self.appEventsDropDeterminer = appEventsDropDeterminer
    self.appEventsStateProvider = appEventsStateProvider
    self.appEventsStateStore = appEventsStateStore
    self.appEventsUtility = appEventsUtility
    self.appLinkEventPoster = appLinkEventPoster
    self.appLinkFactory = appLinkFactory
    self.appLinkResolver = appLinkResolver
    self.appLinkTargetFactory = appLinkTargetFactory
    self.appLinkURLFactory = appLinkURLFactory
    self.atePublisherFactory = atePublisherFactory
    self.authenticationTokenWallet = authenticationTokenWallet
    self.backgroundEventLogger = backgroundEventLogger
    self.capiReporter = capiReporter
    self.codelessIndexer = codelessIndexer
    self.crashHandler = crashHandler
    self.crashObserver = crashObserver
    self.dataExtractor = dataExtractor
    self.defaultDataStore = defaultDataStore
    self.deviceInformationProvider = deviceInformationProvider
    self.dialogConfigurationMapBuilder = dialogConfigurationMapBuilder
    self.errorConfigurationProvider = errorConfigurationProvider
    self.errorFactory = errorFactory
    self.errorReporter = errorReporter
    self.eventDeactivationManager = eventDeactivationManager
    self.eventLogger = eventLogger
    self.featureChecker = featureChecker
    self.featureExtractor = featureExtractor
    self.fileManager = fileManager
    self.gateKeeperManager = gateKeeperManager
    self.getApplicationActivationNotifier = getApplicationActivationNotifier
    self.graphRequestConnectionFactory = graphRequestConnectionFactory
    self.graphRequestFactory = graphRequestFactory
    self.impressionLoggerFactory = impressionLoggerFactory
    self.infoDictionaryProvider = infoDictionaryProvider
    self.internalURLOpener = internalURLOpener
    self.internalUtility = internalUtility
    self.logger = logger
    self.loggerFactory = loggerFactory
    self.macCatalystDeterminator = macCatalystDeterminator
    self.metadataIndexer = metadataIndexer
    self.modelManager = modelManager
    self.notificationCenter = notificationCenter
    self.operatingSystemVersionComparer = operatingSystemVersionComparer
    self.paymentObserver = paymentObserver
    self.piggybackManager = piggybackManager
    self.profileSetter = profileSetter
    self.restrictiveDataFilterManager = restrictiveDataFilterManager
    self.rulesFromKeyProvider = rulesFromKeyProvider
    self.serverConfigurationProvider = serverConfigurationProvider
    self.sessionDataTaskProvider = sessionDataTaskProvider
    self.settings = settings
    self.skAdNetworkReporter = skAdNetworkReporter
    self.suggestedEventsIndexer = suggestedEventsIndexer
    self.swizzler = swizzler
    self.timeSpentRecorder = timeSpentRecorder
    self.tokenCache = tokenCache
    self.urlHoster = urlHoster
    self.urlSessionProxyFactory = urlSessionProxyFactory
    self.userDataStore = userDataStore
    self.userIDProvider = userIDProvider
    self.webViewProvider = webViewProvider
    self.aemManager = aemManager
    self.protectedModeManager = protectedModeManager
    self.macaRuleMatchingManager = macaRuleMatchingManager
  }

  // MARK: - Default components

  static let `default`: CoreKitComponents = {
    let graphRequestFactory: GraphRequestFactoryProtocol = GraphRequestFactory()
    let atePublisherFactory: _ATEPublisherCreating = _ATEPublisherFactory(
      dataStore: UserDefaults.standard,
      graphRequestFactory: graphRequestFactory,
      settings: Settings.shared,
      deviceInformationProvider: _AppEventsDeviceInfo.shared
    )
    let crashObserver: CrashObserving = _CrashObserver(
      featureChecker: _FeatureManager.shared,
      graphRequestFactory: graphRequestFactory,
      settings: Settings.shared,
      crashHandler: CrashHandler.shared
    )
    let impressionLoggerFactory: _ImpressionLoggerFactoryProtocol = _ImpressionLoggerFactory(
      graphRequestFactory: graphRequestFactory,
      eventLogger: AppEvents.shared,
      notificationCenter: NotificationCenter.default,
      accessTokenWallet: AccessToken.self
    )
    let loggerFactory: _LoggerCreating = _LoggerFactory()

    _PaymentProductRequestorFactory.setDependencies(
      .init(
        settings: Settings.shared,
        eventLogger: AppEvents.shared,
        gateKeeperManager: _GateKeeperManager.self,
        store: UserDefaults.standard,
        loggerFactory: loggerFactory,
        productsRequestFactory: _ProductRequestFactory(),
        appStoreReceiptProvider: Bundle(for: ApplicationDelegate.self)
      )
    )

    let paymentProductRequestorFactory: _PaymentProductRequestorCreating = _PaymentProductRequestorFactory()

    let paymentObserver: _PaymentObserving = _PaymentObserver(
      paymentQueue: SKPaymentQueue.default(),
      paymentProductRequestorFactory: paymentProductRequestorFactory
    )
    let piggybackManager: _GraphRequestPiggybackManaging = GraphRequestPiggybackManager()
    let timeSpentRecorder: (_SourceApplicationTracking & _TimeSpentRecording) = _TimeSpentData(
      eventLogger: AppEvents.shared,
      serverConfigurationProvider: _ServerConfigurationManager.shared
    )

    let keychainStoreFactory: KeychainStoreProviding = KeychainStoreFactory()
    let keychainService = "\(DefaultKeychainServicePrefix).\(Bundle.main.bundleIdentifier ?? "nil")"
    let keychainStore: KeychainStoreProtocol = keychainStoreFactory.createKeychainStore(
      service: keychainService,
      accessGroup: nil
    )
    var tokenCache = TokenCache()
    tokenCache.setDependencies(
      .init(
        settings: Settings.shared,
        keychainStore: keychainStore,
        dataStore: UserDefaults.standard
      )
    )

    let userDataStore: _UserDataPersisting = _UserDataStore()
    let capiReporter: CAPIReporter = AppEventsCAPIManager.shared

    let accessTokenExpirer: _AccessTokenExpiring = _AccessTokenExpirer(notificationCenter: NotificationCenter.default)
    let accessTokenWallet: (_AccessTokenProviding & _TokenStringProviding).Type = AccessToken.self
    let advertiserIDProvider: _AdvertiserIDProviding = _AppEventsUtility.shared
    let appEvents: _SourceApplicationTracking & _AppEventsConfiguring & _ApplicationLifecycleObserving
      & _ApplicationActivating & _ApplicationStateSetting & EventLogging = AppEvents.shared
    let appEventsConfigurationProvider: _AppEventsConfigurationProviding = _AppEventsConfigurationManager.shared
    let appEventsStateProvider: _AppEventsStateProviding = AppEventsStateFactory()
    let appEventsStateStore: _AppEventsStatePersisting = _AppEventsStateManager.shared
    let appEventsUtility: _AppEventDropDetermining & _AppEventParametersExtracting & _AppEventsUtility
      & _LoggingNotifying = _AppEventsUtility.shared
    let authenticationTokenWallet: _AuthenticationTokenProviding.Type = AuthenticationToken.self
    let crashHandler: CrashHandlerProtocol = CrashHandler.shared
    let defaultDataStore: DataPersisting = UserDefaults.standard
    let deviceInformationProvider: _DeviceInformationProviding = _AppEventsDeviceInfo.shared
    let dialogConfigurationMapBuilder: _DialogConfigurationMapBuilding = _DialogConfigurationMapBuilder()
    let errorConfigurationProvider: _ErrorConfigurationProviding = _ErrorConfigurationProvider()
    let errorFactory: ErrorCreating = _ErrorFactory()
    let errorReporter: ErrorReporting = ErrorReporter.shared
    let eventDeactivationManager: _AppEventsParameterProcessing & _EventsProcessing = EventDeactivationManager()
    let eventLogger: EventLogging = AppEvents.shared
    let featureChecker: FeatureChecking & _FeatureDisabling = _FeatureManager.shared
    let gateKeeperManager: _GateKeeperManaging.Type = _GateKeeperManager.self
    let getApplicationActivationNotifier: () -> Any = { ApplicationDelegate.shared }
    let graphRequestConnectionFactory: GraphRequestConnectionFactoryProtocol = GraphRequestConnectionFactory()
    let infoDictionaryProvider: InfoDictionaryProviding = Bundle.main
    let internalUtility: InternalUtilityProtocol = InternalUtility.shared
    let logger: Logging.Type = _Logger.self
    let macCatalystDeterminator: _MacCatalystDetermining = ProcessInfo.processInfo
    let notificationCenter: _NotificationPosting & NotificationDelivering = NotificationCenter.default
    let operatingSystemVersionComparer: _OperatingSystemVersionComparing = ProcessInfo.processInfo
    let restrictiveDataFilterManager: _AppEventsParameterProcessing & _EventsProcessing =
      _RestrictiveDataFilterManager(serverConfigurationProvider: _ServerConfigurationManager.shared)
    let serverConfigurationProvider: _ServerConfigurationProviding = _ServerConfigurationManager.shared
    let settings: SettingsProtocol & SettingsLogging = Settings.shared
    let urlSessionProxyFactory: _URLSessionProxyProviding = _URLSessionProxyFactory()
    let protectedModeManager: _AppEventsParameterProcessing = ProtectedModeManager()
    let macaRuleMatchingManager: MACARuleMatching = MACARuleMatchingManager()

    var aemNetworker: AEMNetworking?
    if #available(iOS 14, *) {
      aemNetworker = AEMNetworker()
    }

    var skAdNetworkReporter: (_AppEventsReporter & SKAdNetworkReporting)?
    skAdNetworkReporter = _SKAdNetworkReporter(
      graphRequestFactory: graphRequestFactory,
      dataStore: UserDefaults.standard,
      conversionValueUpdater: SKAdNetwork.self
    )

    let metaIndexer: _MetadataIndexing = _MetadataIndexer(userDataStore: userDataStore, swizzler: _Swizzler.self)
    let suggestedEventsIndexer: _SuggestedEventsIndexerProtocol = _SuggestedEventsIndexer(
      graphRequestFactory: graphRequestFactory,
      serverConfigurationProvider: _ServerConfigurationManager.shared,
      swizzler: _Swizzler.self,
      settings: Settings.shared,
      eventLogger: AppEvents.shared,
      featureExtractor: _FeatureExtractor.self,
      eventProcessor: _ModelManager.shared
    )
    let backgroundEventLogger: BackgroundEventLogging = BackgroundEventLogger()

    return CoreKitComponents(
      accessTokenExpirer: accessTokenExpirer,
      accessTokenWallet: accessTokenWallet,
      advertiserIDProvider: advertiserIDProvider,
      aemNetworker: aemNetworker,
      aemReporter: AEMReporter.self,
      appEventParametersExtractor: _AppEventsUtility.shared,
      appEvents: appEvents,
      appEventsConfigurationProvider: appEventsConfigurationProvider,
      appEventsDropDeterminer: _AppEventsUtility.shared,
      appEventsStateProvider: appEventsStateProvider,
      appEventsStateStore: appEventsStateStore,
      appEventsUtility: appEventsUtility,
      appLinkEventPoster: _MeasurementEvent(),
      appLinkFactory: AppLinkFactory(),
      appLinkResolver: WebViewAppLinkResolver.shared,
      appLinkTargetFactory: AppLinkTargetFactory(),
      appLinkURLFactory: AppLinkURLFactory(),
      atePublisherFactory: atePublisherFactory,
      authenticationTokenWallet: authenticationTokenWallet,
      backgroundEventLogger: backgroundEventLogger,
      capiReporter: capiReporter,
      codelessIndexer: _CodelessIndexer.self,
      crashHandler: crashHandler,
      crashObserver: crashObserver,
      dataExtractor: NSData.self,
      defaultDataStore: defaultDataStore,
      deviceInformationProvider: deviceInformationProvider,
      dialogConfigurationMapBuilder: dialogConfigurationMapBuilder,
      errorConfigurationProvider: errorConfigurationProvider,
      errorFactory: errorFactory,
      errorReporter: errorReporter,
      eventDeactivationManager: eventDeactivationManager,
      eventLogger: eventLogger,
      featureChecker: featureChecker,
      featureExtractor: _FeatureExtractor.self,
      fileManager: FileManager.default,
      gateKeeperManager: gateKeeperManager,
      getApplicationActivationNotifier: getApplicationActivationNotifier,
      graphRequestConnectionFactory: graphRequestConnectionFactory,
      graphRequestFactory: graphRequestFactory,
      impressionLoggerFactory: impressionLoggerFactory,
      infoDictionaryProvider: infoDictionaryProvider,
      internalURLOpener: CoreUIApplication.shared,
      internalUtility: internalUtility,
      logger: logger,
      loggerFactory: loggerFactory,
      macCatalystDeterminator: macCatalystDeterminator,
      metadataIndexer: metaIndexer,
      modelManager: _ModelManager.shared,
      notificationCenter: notificationCenter,
      operatingSystemVersionComparer: operatingSystemVersionComparer,
      paymentObserver: paymentObserver,
      piggybackManager: piggybackManager,
      profileSetter: Profile.self,
      restrictiveDataFilterManager: restrictiveDataFilterManager,
      rulesFromKeyProvider: _ModelManager.shared,
      serverConfigurationProvider: serverConfigurationProvider,
      sessionDataTaskProvider: URLSession.shared,
      settings: settings,
      skAdNetworkReporter: skAdNetworkReporter,
      suggestedEventsIndexer: suggestedEventsIndexer,
      swizzler: _Swizzler.self,
      timeSpentRecorder: timeSpentRecorder,
      tokenCache: tokenCache,
      urlHoster: InternalUtility.shared,
      urlSessionProxyFactory: urlSessionProxyFactory,
      userDataStore: userDataStore,
      userIDProvider: AppEvents.shared,
      webViewProvider: _WebViewFactory(),
      aemManager: _AEMManager.shared,
      protectedModeManager: protectedModeManager,
      macaRuleMatchingManager: macaRuleMatchingManager
    )
  }()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@testable import FBAEMKit

final class CoreKitConfigurator: CoreKitConfiguring {

  let components: CoreKitComponents

  init(components: CoreKitComponents) {
    self.components = components
  }

  func performConfiguration() {
    configureSettings()
    configureAccessToken()
    configureAppEvents()
    configureAppEventsConfigurationManager()
    configureAppEventsDeviceInfo()
    configureAppEventsState()
    configureAppEventsUtility()
    configureAuthenticationToken()
    configureButton()
    configureGatekeeperManager()
    configureGraphRequest()
    configureGraphRequestConnection()
    configureImpressionLoggingButton()
    configureInstrumentManager()
    configureInternalUtility()
    configureServerConfigurationManager()
    configureCloudBridge()
    configureAEMReporter()
    configureAEMManager()
    configureNonTVOSAppEvents()
    configureAppLinkNavigation()
    configureAppLinkURL()
    configureAppLinkUtility()
    configureAuthenticationStatusUtility()
    configureBridgeAPIRequest()
    configureCodelessIndexer()
    configureCrashShield()
    configureFeatureExtractor()
    configureModelManager()
    configureProfile()
    configureWebDialogView()
  }
}

// swiftformat:disable:next extensionaccesscontrol
private extension CoreKitConfigurator {
  func configureAccessToken() {
    AccessToken.configure(
      tokenCache: components.tokenCache,
      graphRequestConnectionFactory: components.graphRequestConnectionFactory,
      graphRequestPiggybackManager: components.piggybackManager,
      errorFactory: components.errorFactory
    )
  }

  func configureAEMReporter() {
    if #available(iOS 14, *) {
      AEMReporter.configure(
        networker: components.aemNetworker,
        appID: components.settings.appID,
        reporter: components.skAdNetworkReporter
      )
    }
  }

  func configureAEMManager() {
    if #available(iOS 14, *) {
      _AEMManager.shared.configure(
        swizzler: components.swizzler,
        reporter: components.aemReporter,
        eventLogger: components.eventLogger,
        crashHandler: components.crashHandler,
        featureChecker: components.featureChecker,
        appEventsUtility: components.appEventsUtility
      )
    }
  }

  func configureAppEvents() {
    AppEvents.shared.configure(
      gateKeeperManager: components.gateKeeperManager,
      appEventsConfigurationProvider: components.appEventsConfigurationProvider,
      serverConfigurationProvider: components.serverConfigurationProvider,
      graphRequestFactory: components.graphRequestFactory,
      featureChecker: components.featureChecker,
      primaryDataStore: components.defaultDataStore,
      logger: components.logger,
      settings: components.settings,
      paymentObserver: components.paymentObserver,
      timeSpentRecorder: components.timeSpentRecorder,
      appEventsStateStore: components.appEventsStateStore,
      eventDeactivationParameterProcessor: components.eventDeactivationManager,
      restrictiveDataFilterParameterProcessor: components.restrictiveDataFilterManager,
      atePublisherFactory: components.atePublisherFactory,
      appEventsStateProvider: components.appEventsStateProvider,
      advertiserIDProvider: components.advertiserIDProvider,
      userDataStore: components.userDataStore,
      appEventsUtility: components.appEventsUtility,
      internalUtility: components.internalUtility,
      capiReporter: components.capiReporter,
      protectedModeManager: components.protectedModeManager,
      macaRuleMatchingManager: components.macaRuleMatchingManager
    )
  }

  func configureAppEventsConfigurationManager() {
    _AppEventsConfigurationManager.shared.configure(
      store: components.defaultDataStore,
      settings: components.settings,
      graphRequestFactory: components.graphRequestFactory,
      graphRequestConnectionFactory: components.graphRequestConnectionFactory
    )
  }

  func configureAppEventsDeviceInfo() {
    _AppEventsDeviceInfo.shared.configure(settings: components.settings)
  }

  func configureAppEventsState() {
    _AppEventsState.eventProcessors = [
      components.eventDeactivationManager,
      components.restrictiveDataFilterManager,
    ]
  }

  func configureAppEventsUtility() {
    _AppEventsUtility.shared.configure(
      appEventsConfigurationProvider: components.appEventsConfigurationProvider,
      deviceInformationProvider: components.deviceInformationProvider,
      settings: components.settings,
      internalUtility: components.internalUtility,
      errorFactory: components.errorFactory,
      dataStore: components.defaultDataStore
    )
  }

  func configureAppLinkNavigation() {
    AppLinkNavigation.setDependencies(
      .init(
        settings: components.settings,
        urlOpener: components.internalURLOpener,
        appLinkEventPoster: components.appLinkEventPoster,
        appLinkResolver: components.appLinkResolver
      )
    )
  }

  func configureAppLinkURL() {
    AppLinkURL.configure(
      settings: components.settings,
      appLinkFactory: components.appLinkFactory,
      appLinkTargetFactory: components.appLinkTargetFactory,
      appLinkEventPoster: components.appLinkEventPoster
    )
  }

  func configureAppLinkUtility() {
    AppLinkUtility.configure(
      graphRequestFactory: components.graphRequestFactory,
      infoDictionaryProvider: components.infoDictionaryProvider,
      settings: components.settings,
      appEventsConfigurationProvider: components.appEventsConfigurationProvider,
      advertiserIDProvider: components.advertiserIDProvider,
      appEventsDropDeterminer: components.appEventsDropDeterminer,
      appEventParametersExtractor: components.appEventParametersExtractor,
      appLinkURLFactory: components.appLinkURLFactory,
      userIDProvider: components.userIDProvider,
      userDataStore: components.userDataStore
    )
  }

  func configureAuthenticationStatusUtility() {
    _AuthenticationStatusUtility.configure(
      profileSetter: components.profileSetter,
      sessionDataTaskProvider: components.sessionDataTaskProvider,
      accessTokenWallet: components.accessTokenWallet,
      authenticationTokenWallet: components.authenticationTokenWallet
    )
  }

  func configureAuthenticationToken() {
    AuthenticationToken.tokenCache = components.tokenCache
  }

  func configureBridgeAPIRequest() {
    _BridgeAPIRequest.configure(
      internalURLOpener: components.internalURLOpener,
      internalUtility: components.internalUtility,
      settings: components.settings
    )
  }

  func configureButton() {
    FBButton.configure(
      applicationActivationNotifier: components.getApplicationActivationNotifier(),
      eventLogger: components.eventLogger,
      accessTokenProvider: components.accessTokenWallet
    )
  }

  func configureCloudBridge() {
    FBSDKAppEventsCAPIManager.shared.configure(
      factory: components.graphRequestFactory,
      settings: components.settings
    )
  }

  func configureCodelessIndexer() {
    _CodelessIndexer.configure(
      graphRequestFactory: components.graphRequestFactory,
      serverConfigurationProvider: components.serverConfigurationProvider,
      dataStore: components.defaultDataStore,
      graphRequestConnectionFactory: components.graphRequestConnectionFactory,
      swizzler: components.swizzler,
      settings: components.settings,
      advertiserIDProvider: components.advertiserIDProvider
    )
  }

  func configureCrashShield() {
    _CrashShield.configure(
      settings: components.settings,
      graphRequestFactory: components.graphRequestFactory,
      featureChecking: components.featureChecker
    )
  }

  func configureFeatureExtractor() {
    _FeatureExtractor.configure(rulesFromKeyProvider: components.rulesFromKeyProvider)
  }

  func configureGatekeeperManager() {
    _GateKeeperManager.configure(
      settings: components.settings,
      graphRequestFactory: components.graphRequestFactory,
      graphRequestConnectionFactory: components.graphRequestConnectionFactory,
      store: components.defaultDataStore
    )
  }

  func configureGraphRequest() {
    GraphRequest.configure(
      settings: components.settings,
      currentAccessTokenStringProvider: components.accessTokenWallet,
      graphRequestConnectionFactory: components.graphRequestConnectionFactory
    )
  }

  func configureGraphRequestConnection() {
    GraphRequestConnection.configure(
      urlSessionProxyFactory: components.urlSessionProxyFactory,
      errorConfigurationProvider: components.errorConfigurationProvider,
      piggybackManager: components.piggybackManager,
      settings: components.settings,
      graphRequestConnectionFactory: components.graphRequestConnectionFactory,
      eventLogger: components.eventLogger,
      operatingSystemVersionComparer: components.operatingSystemVersionComparer,
      macCatalystDeterminator: components.macCatalystDeterminator,
      accessTokenProvider: components.accessTokenWallet,
      errorFactory: components.errorFactory,
      authenticationTokenProvider: components.authenticationTokenWallet
    )

    GraphRequestConnection.setCanMakeRequests()
  }

  func configureImpressionLoggingButton() {
    ImpressionLoggingButton.configure(impressionLoggerFactory: components.impressionLoggerFactory)
  }

  func configureInstrumentManager() {
    _InstrumentManager.shared.configure(
      featureChecker: components.featureChecker,
      settings: components.settings,
      crashObserver: components.crashObserver,
      errorReporter: components.errorReporter,
      crashHandler: components.crashHandler
    )
  }

  func configureInternalUtility() {
    InternalUtility.shared.configure(
      infoDictionaryProvider: components.infoDictionaryProvider,
      loggerFactory: components.loggerFactory,
      settings: components.settings,
      errorFactory: components.errorFactory
    )
  }

  func configureModelManager() {
    let settings = components.settings
    _ModelManager.shared.configure(
      featureChecker: components.featureChecker,
      graphRequestFactory: components.graphRequestFactory,
      fileManager: components.fileManager,
      store: components.defaultDataStore,
      getAppID: { [settings] in settings.appID ?? "" },
      dataExtractor: components.dataExtractor,
      gateKeeperManager: components.gateKeeperManager,
      suggestedEventsIndexer: components.suggestedEventsIndexer,
      featureExtractor: components.featureExtractor
    )
  }

  func configureNonTVOSAppEvents() {
    AppEvents.shared.configureNonTVComponents(
      onDeviceMLModelManager: components.modelManager,
      metadataIndexer: components.metadataIndexer,
      skAdNetworkReporter: components.skAdNetworkReporter,
      codelessIndexer: components.codelessIndexer,
      swizzler: components.swizzler,
      aemReporter: components.aemReporter
    )
  }

  func configureServerConfigurationManager() {
    _ServerConfigurationManager.shared.configure(
      graphRequestFactory: components.graphRequestFactory,
      graphRequestConnectionFactory: components.graphRequestConnectionFactory,
      dialogConfigurationMapBuilder: components.dialogConfigurationMapBuilder
    )
  }

  func configureSettings() {
    Settings.shared.setDependencies(
      .init(
        appEventsConfigurationProvider: components.appEventsConfigurationProvider,
        dataStore: components.defaultDataStore,
        eventLogger: components.eventLogger,
        infoDictionaryProvider: components.infoDictionaryProvider
      )
    )
  }

  func configureProfile() {
    Profile.setDependencies(
      .init(
        accessTokenProvider: components.accessTokenWallet,
        dataStore: components.defaultDataStore,
        graphRequestFactory: components.graphRequestFactory,
        notificationCenter: components.notificationCenter,
        settings: components.settings,
        urlHoster: components.urlHoster
      )
    )
  }

  func configureWebDialogView() {
    FBWebDialogView.configure(
      webViewProvider: components.webViewProvider,
      urlOpener: components.internalURLOpener,
      errorFactory: components.errorFactory
    )
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

protocol CoreKitConfiguring {
  func performConfiguration()
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@dynamicMemberLookup
protocol DependentAsObject: AnyObject {
  associatedtype ObjectDependencies

  var configuredDependencies: ObjectDependencies? { get set }
  var defaultDependencies: ObjectDependencies? { get }

  func setDependencies(_ dependencies: ObjectDependencies)

  #if DEBUG
  func resetDependencies()
  #endif
}

extension DependentAsObject {
  func setDependencies(_ dependencies: ObjectDependencies) {
    configuredDependencies = dependencies
  }

  #if DEBUG
  func resetDependencies() {
    configuredDependencies = nil
  }
  #endif

  func getDependencies() throws -> ObjectDependencies {
    guard let dependencies = configuredDependencies ?? defaultDependencies else {
      throw MissingDependenciesError(for: Self.self)
    }

    return dependencies
  }

  subscript<Dependency>(dynamicMember keyPath: KeyPath<ObjectDependencies, Dependency>) -> Dependency? {
    try? getDependencies()[keyPath: keyPath]
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@dynamicMemberLookup
protocol DependentAsType {
  associatedtype TypeDependencies

  static var configuredDependencies: TypeDependencies? { get set }
  static var defaultDependencies: TypeDependencies? { get }

  static func setDependencies(_ dependencies: TypeDependencies)

  #if DEBUG
  static func resetDependencies()
  #endif
}

extension DependentAsType {
  static func setDependencies(_ dependencies: TypeDependencies) {
    configuredDependencies = dependencies
  }

  #if DEBUG
  static func resetDependencies() {
    configuredDependencies = nil
  }
  #endif

  static func getDependencies() throws -> TypeDependencies {
    guard let dependencies = configuredDependencies ?? defaultDependencies else {
      throw MissingDependenciesError(for: Self.self)
    }

    return dependencies
  }

  static subscript<Dependency>(dynamicMember keyPath: KeyPath<TypeDependencies, Dependency>) -> Dependency? {
    try? getDependencies()[keyPath: keyPath]
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

@dynamicMemberLookup
protocol DependentAsValue {
  associatedtype ValueDependencies

  var configuredDependencies: ValueDependencies? { get set }
  var defaultDependencies: ValueDependencies? { get }

  mutating func setDependencies(_ dependencies: ValueDependencies)

  #if DEBUG
  mutating func resetDependencies()
  #endif
}

extension DependentAsValue {
  mutating func setDependencies(_ dependencies: ValueDependencies) {
    configuredDependencies = dependencies
  }

  #if DEBUG
  mutating func resetDependencies() {
    configuredDependencies = nil
  }
  #endif

  func getDependencies() throws -> ValueDependencies {
    guard let dependencies = configuredDependencies ?? defaultDependencies else {
      throw MissingDependenciesError(for: Self.self)
    }

    return dependencies
  }

  subscript<Dependency>(dynamicMember keyPath: KeyPath<ValueDependencies, Dependency>) -> Dependency? {
    try? getDependencies()[keyPath: keyPath]
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

struct MissingDependenciesError<Dependent>: Error, CustomStringConvertible {
  private let dependentType: Dependent.Type

  init(for dependentType: Dependent.Type) {
    self.dependentType = dependentType
  }

  var description: String {
    "The dependencies for the type '\(dependentType)' or an instance of it have not been set"
  }
}


/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the
 * LICENSE file in the root directory of this source tree.
 */

import FBSDKCoreKit_Basics
import Foundation

/**
 Internal Type exposed to facilitate transition to Swift.
 API Subject to change or removal without warning. Do not use.

 @warning INTERNAL - DO NOT USE
 */
@objcMembers
@objc(FBSDKFeatureManager)
public final class _FeatureManager: NSObject, FeatureChecking, _FeatureDisabling {
  let featureManagerPrefix = "com.facebook.sdk:FBSDKFeatureManager.FBSDKFeature"

  public static let shared = _FeatureManager()

  public func isEnabled(_ feature: SDKFeature) -> Bool {
    if SDKFeature.core == feature || SDKFeature.none == feature {
      return true
    }

    guard let dependencies = try? Self.getDependencies() else {
      return false
    }

    // check if the feature is locally disabled by Crash Shield first
    let version = dependencies.store.fb_string(forKey: storageKey(for: feature))
    if version == dependencies.settings.sdkVersion {
      return false
    }

    guard let parentFeature = getParentFeature(for: feature) else {
      return false
    }

    if parentFeature == feature {
      return checkGateKeeper(for: feature)
    } else {
      return isEnabled(parentFeature) && checkGateKeeper(for: feature)
    }
  }

  public func check(_ feature: SDKFeature, completionBlock: @escaping FBSDKFeatureManagerBlock) {
    guard let dependencies = try? Self.getDependencies() else {
      completionBlock(false)
      return
    }

    // check if the feature is locally disabled by Crash Shield first
    let version = dependencies.store.fb_string(forKey: storageKey(for: feature))
    if version == dependencies.settings.sdkVersion {
      completionBlock(false)
      return
    }

    // check gk
    dependencies.gateKeeperManager.loadGateKeepers { _ in
      completionBlock(self.isEnabled(feature))
    }
  }

  public func disableFeature(_ feature: SDKFeature) {
    guard let dependencies = try? Self.getDependencies() else {
      return
    }

    dependencies.store.fb_setObject(dependencies.settings.sdkVersion, forKey: storageKey(for: feature))
  }

  func storageKey(for feature: SDKFeature) -> String {
    featureManagerPrefix + featureName(for: feature)
  }

  func checkGateKeeper(for feature: SDKFeature) -> Bool {
    guard let gateKeeperManager = try? Self.getDependencies().gateKeeperManager else {
      return false
    }

    let featureName = featureName(for: feature)
    let key = "FBSDKFeature\(featureName)"
    let defaultValue = defaultStatus(for: feature)

    return gateKeeperManager.bool(forKey: key, defaultValue: defaultValue)
  }

  func defaultStatus(for feature: SDKFeature) -> Bool {
    switch feature {
    case
      .restrictiveDataFiltering,
      .eventDeactivation,
      .instrument,
      .crashReport,
      .crashShield,
      .errorReport,
      .AAM,
      .privacyProtection,
      .suggestedEvents,
      .intelligentIntegrity,
      .modelRequest,
      .ateLogging,
      .AEM,
      .aemConversionFiltering,
      .aemCatalogMatching,
      .aemAdvertiserRuleMatchInServer,
      .aemAutoSetup,
      .aemAutoSetupProxy,
      .appEventsCloudbridge,
      .skAdNetwork,
      .skAdNetworkConversionValue,
      .protectedMode,
      .macaRuleMatching:
      return false
    case .none, .login, .share, .core, .appEvents, .codelessEvents, .gamingServices:
      return true
    @unknown default:
      return false
    }
  }

  func getParentFeature(for feature: SDKFeature) -> SDKFeature? {
    if feature.rawValue & 0xFF > 0 {
      return SDKFeature(rawValue: feature.rawValue & 0xFFFFFF00)
    } else if feature.rawValue & 0xFF00 > 0 {
      return SDKFeature(rawValue: feature.rawValue & 0xFFFF0000)
    } else if feature.rawValue & 0xFF0000 > 0 {
      return SDKFeature(rawValue: feature.rawValue & 0xFF000000)
    } else {
      return SDKFeature(rawValue: 0)
    }
  }

  // swiftlint:disable:next cyclomatic_complexity
  func featureName(for feature: SDKFeature) -> String {
    var featureName: String

    switch feature {
    case .none: featureName = "NONE"
    case .core: featureName = "CoreKit"
    case .appEvents: featureName = "AppEvents"
    case .codelessEvents: featureName = "CodelessEvents"
    case .restrictiveDataFiltering: featureName = "RestrictiveDataFiltering"
    case .AAM: featureName = "AAM"
    case .privacyProtection: featureName = "PrivacyProtection"
    case .suggestedEvents: featureName = "SuggestedEvents"
    case .intelligentIntegrity: featureName = "IntelligentIntegrity"
    case .modelRequest: featureName = "ModelRequest"
    case .eventDeactivation: featureName = "EventDeactivation"
    case .skAdNetwork: featureName = "SKAdNetwork"
    case .skAdNetworkConversionValue: featureName = "SKAdNetworkConversionValue"
    case .instrument: featureName = "Instrument"
    case .crashReport: featureName = "CrashReport"
    case .crashShield: featureName = "CrashShield"
    case .errorReport: featureName = "ErrorReport"
    case .ateLogging: featureName = "ATELogging"
    case .AEM: featureName = "AEM"
    case .aemConversionFiltering: featureName = "AEMConversionFiltering"
    case .aemCatalogMatching: featureName = "AEMCatalogMatching"
    case .aemAdvertiserRuleMatchInServer: featureName = "AEMAdvertiserRuleMatchInServer"
    case .aemAutoSetup: featureName = "AppAemAutoSetUp"
    case .aemAutoSetupProxy: featureName = "AppAemAutoSetUpProxy"
    case .appEventsCloudbridge: featureName = "AppEventsCloudbridge"
    case .login: featureName = "LoginKit"
    case .share: featureName = "ShareKit"
    case .gamingServices: featureName = "GamingServicesKit"
    case .protectedMode: featureName = "ProtectedMode"
    case .macaRuleMatching: featureName = "MACARuleMatching"
    @unknown default: featureName = "NONE"
    }

    return featureName
  }
}

extension _FeatureManager: DependentAsType {
  struct TypeDependencies {
    var gateKeeperManager: _GateKeeperManaging.Type
    var settings: SettingsProtocol
    var store: DataPersisting
  }

  static var configuredDependencies: TypeDependencies?

  static var defaultDependencies: TypeDependencies? = TypeDependencies(
    gateKeeperManager: _GateKeeperManager.self,
    settings: Settings.shared,
    store: UserDefaults.standard
  )
}