// generated by bun run generate from the SwiftUI SDK and codegen/catalog.ts.
// edit the generator or catalog, then regenerate.
import SwiftUI
import UIKit
import AuthenticationServices

private final class SignInWithAppleButtonModel: ObservableObject {
  @Published var requestedScopes: [String] = []
  @Published var nonce: String = ""
  @Published var label: String = "signIn"
  @Published var accessibility = OneNativeAccessibility()
  @Published var swiftStyle = OneNativeStyle()
  var active = false
  var onSDKEvent: ((String, String) -> Void)?
  func emitSDKEvent(_ name: String, _ value: String) { if active { onSDKEvent?(name, value) } }

  var onCompletion: ((String, String, String, String, String, String, String, String, Int) -> Void)?
  private var completionCount = 0
  func completion(_ type: String, _ user: String, _ email: String, _ givenName: String, _ familyName: String, _ identityToken: String, _ authorizationCode: String, _ message: String) {
    guard active else { return }
    completionCount += 1
    onCompletion?(type, user, email, givenName, familyName, identityToken, authorizationCode, message, completionCount)
  }
}
@objcMembers public final class OneNativeSignInWithAppleButtonView: UIView, OneNativeComposable {
  public var onSDKEvent: ((String, String) -> Void)?

  public var onCompletion: ((String, String, String, String, String, String, String, String, Int) -> Void)?
  private var model = SignInWithAppleButtonModel()
  public var onHeight: ((CGFloat) -> Void)?
  private var controller: OneNativeHostingController<OneNativeMeasuredStandalone<SignInWithAppleButtonContent>>?
  public override init(frame: CGRect) { super.init(frame: frame) }
  required init?(coder: NSCoder) { fatalError("init(coder:) is unavailable") }
  public func configureAccessibility(_ label: String, hint: String, value: String, identifier: String) {
    let next = OneNativeAccessibility(label: label, hint: hint, value: value, identifier: identifier)
    if model.accessibility != next { model.accessibility = next }
  }
  public func configureStyle(_ style: [String: Any]) {
    let next = OneNativeStyle(dictionary: style)
    if model.swiftStyle != next {
      let previousRows = model.swiftStyle.sdkModifiers.filter { $0.first?.hasPrefix("listRow") == true }
      let nextRows = next.sdkModifiers.filter { $0.first?.hasPrefix("listRow") == true }
      model.swiftStyle = next
      if previousRows != nextRows { compositionParent?.refreshRow(for: self) }
    }
  }
  public func configure(_ nonce: String, label: String) {
    if model.nonce != nonce { model.nonce = nonce }
    if model.label != label { model.label = label }
  }
  public func setRequestedScopes(_ items: [String]) {
    if let unknown = items.first(where: { $0 != "fullName" && $0 != "email" }) {
    model.completion("failed", "", "", "", "", "", "", "unknown requested scope: \(unknown)")
    return
  }
  if model.requestedScopes != items { model.requestedScopes = items }
  }

  private weak var compositionParent: OneNativeCompositionParent?
  public func compositionContent() -> AnyView { AnyView(SignInWithAppleButtonContent(model: model)) }
  // composed, activation follows the parent: a subtree mounted before root attachment
  // stays silent until the root attaches.
  public func composeInto(_ parent: OneNativeCompositionParent) {
    controller?.detach(); controller = nil
    compositionParent = parent
    bindCallbacks()
    model.active = parent.compositionActive
  }
  public func decompose() { compositionParent = nil; model.active = false }
  public func propagateActive(_ active: Bool) { model.active = active }
  public override func didMoveToWindow() { super.didMoveToWindow(); updateHost() }
  public override func layoutSubviews() { super.layoutSubviews(); updateHost() }
  private func bindCallbacks() {
    model.onSDKEvent = { [weak self] name, value in self?.onSDKEvent?(name, value) }

    model.onCompletion = { [weak self] type, user, email, givenName, familyName, identityToken, authorizationCode, message, completionCount in self?.onCompletion?(type, user, email, givenName, familyName, identityToken, authorizationCode, message, completionCount) }
  }
  private func updateHost() {
    guard compositionParent == nil else { return }
    model.active = false
    guard window != nil else { controller?.detach(); return }
    if controller == nil {
      bindCallbacks()
      controller = OneNativeHostingController(rootView: OneNativeMeasuredStandalone(content: SignInWithAppleButtonContent(model: model), onHeight: { [weak self] height in self?.onHeight?(height) }))
    }
    controller?.attach(to: self)
    model.active = controller?.isAttached == true
  }
  public func reset() {
    compositionParent = nil
    model.active = false; model.onSDKEvent = nil; model.onCompletion = nil
    controller?.detach(); controller = nil; model = SignInWithAppleButtonModel()
  }
}
private struct SignInWithAppleButtonContent: View {
  @ObservedObject var model: SignInWithAppleButtonModel
  var body: some View {
    SignInWithAppleButtonSurface(model: model)
      .oneNativeAccessibility(model.accessibility)
      .oneNativeStyle(model.swiftStyle, emit: model.emitSDKEvent)
  }
}
// ASAuthorization.Scope is AuthenticationServices' own type rather than its SwiftUI
// overlay's, so the generator does not read it and cannot select these cases.
// swiftc -typecheck against the SDK is what proves each one still exists.
private func oneNativeAppleScopes(_ values: [String]) -> [ASAuthorization.Scope] {
  // unknown strings never reach here: the setter rejects them with a failed
  // completion, so the default only drops what validation already refused.
  values.compactMap { value in
    switch value {
    case "fullName": return .fullName
    case "email": return .email
    default: return nil
    }
  }
}

// the tokens arrive as UTF-8 JWT data. the empty string is the missing-token state the
// caller already handles, so undecodable bytes degrade to it instead of failing the
// completion that carried them.
private func oneNativeTokenString(_ data: Data?) -> String {
  guard let data else { return "" }
  return String(data: data, encoding: .utf8) ?? ""
}

// validate admits only these three, so the default case is unreachable
private func oneNativeAppleButtonLabel(_ value: String) -> SignInWithAppleButton.Label {
  switch value {
  case "continue": return .continue
  case "signUp": return .signUp
  default: return .signIn
  }
}

private struct SignInWithAppleButtonSurface: View {
  @ObservedObject var model: SignInWithAppleButtonModel
  var body: some View {
    SignInWithAppleButton(oneNativeAppleButtonLabel(model.label), onRequest: { request in
      request.requestedScopes = oneNativeAppleScopes(model.requestedScopes)
      if !model.nonce.isEmpty { request.nonce = model.nonce }
    }, onCompletion: { result in
      switch result {
      case .success(let authorization):
        guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential else {
          model.completion("failed", "", "", "", "", "", "", "unexpected credential type")
          return
        }
        model.completion(
          "success",
          credential.user,
          credential.email ?? "",
          credential.fullName?.givenName ?? "",
          credential.fullName?.familyName ?? "",
          oneNativeTokenString(credential.identityToken),
          oneNativeTokenString(credential.authorizationCode),
          ""
        )
      case .failure(let error):
        let nsError = error as NSError
        if nsError.domain == ASAuthorizationErrorDomain,
          nsError.code == ASAuthorizationError.Code.canceled.rawValue {
          model.completion("cancelled", "", "", "", "", "", "", "")
        } else {
          model.completion("failed", "", "", "", "", "", "", error.localizedDescription)
        }
      }
    })
  }
}
