// Copyright 2022-present 650 Industries. All rights reserved.

import ExpoModulesCore
import AuthenticationServices

private class PresentationContextProvider: NSObject, ASWebAuthenticationPresentationContextProviding {
  func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
    #if os(iOS)
    return UIApplication.shared.keyWindow ?? ASPresentationAnchor()
    #else
    return NSApp.mainWindow ?? ASPresentationAnchor()
    #endif
  }
}

final internal class WebAuthSession {
  var authSession: ASWebAuthenticationSession?
  var promise: Promise?
  var isOpen: Bool {
    promise != nil
  }

  // It must be initialized before hand as `ASWebAuthenticationSession` holds it as a weak property
  private var presentationContextProvider = PresentationContextProvider()

  init(authUrl: URL, redirectUrl: URL?, options: AuthSessionOptions) {
    let completionHandler: ASWebAuthenticationSession.CompletionHandler = { callbackUrl, error in
      self.finish(with: [
        "type": callbackUrl != nil ? "success" : "cancel",
        "url": callbackUrl?.absoluteString,
        "error": error?.localizedDescription
      ])
    }

    // iOS 17.4+/macOS 14.4+ supports HTTPS callbacks with host/path matching.
    // Only used when preferUniversalLinks is true, as it requires the app to have
    // the Associated Domains entitlement for the redirect host.
    if options.preferUniversalLinks,
       #available(iOS 17.4, macOS 14.4, *),
       let redirectUrl,
       redirectUrl.scheme == "https",
       let host = redirectUrl.host(percentEncoded: false),
       !host.isEmpty {
      // Use the new callback API for HTTPS universal links
      // Pass an empty string for the path to match any path under the host if no specific path is provided
      self.authSession = ASWebAuthenticationSession(
        url: authUrl,
        callback: .https(host: host, path: redirectUrl.path),
        completionHandler: completionHandler
      )
    } else {
      // Fallback to the old API for custom schemes or older iOS versions
      self.authSession = ASWebAuthenticationSession(
        url: authUrl,
        callbackURLScheme: redirectUrl?.scheme,
        completionHandler: completionHandler
      )
    }
    self.authSession?.prefersEphemeralWebBrowserSession = options.preferEphemeralSession
  }

  func open(_ promise: Promise) {
    authSession?.presentationContextProvider = presentationContextProvider
    authSession?.start()
    self.promise = promise
  }

  func dismiss() {
    authSession?.cancel()
    finish(with: ["type": "dismiss"])
  }

  // MARK: - Private

  private func finish(with result: [String: String?]) {
    promise?.resolve(result)
    promise = nil
    authSession = nil
  }
}
