import SwiftUI
import AuthenticationServices
import Foundation // PersonNameComponentsFormatter

/// "Mit Apple anmelden" — placed below the email/password form on
/// `LoginView`. Wired to the real backend: `POST /api/auth/apple`
/// (`src/app/api/auth/apple/route.ts`) verifies the identity token Apple
/// hands back and returns a session exactly like `/api/auth/login` does —
/// see `AuthAPI.appleSignIn` / `AppState.signInWithApple`.
struct AppleSignInButton: View {
    @Environment(\.colorScheme) private var colorScheme
    @EnvironmentObject private var appState: AppState

    @State private var isSigningIn = false
    /// Local, Apple-specific failures (token decode, user cancelled) — kept
    /// separate from `appState.lastError` so a cancelled Apple sheet doesn't
    /// show a stale email/password error underneath it, or vice versa.
    @State private var localErrorMessage: String?

    var body: some View {
        VStack(spacing: ZenithSpacing.unit * 2) {
            SignInWithAppleButton(.signIn) { request in
                request.requestedScopes = [.fullName, .email]
            } onCompletion: { result in
                handle(result)
            }
            // Apple's Human Interface Guidelines fix this button's own shape
            // and colors — not overridable to match Zenith Silver's 0px
            // corners, and Apple explicitly does not allow restyling it.
            .signInWithAppleButtonStyle(colorScheme == .dark ? .white : .black)
            .frame(maxWidth: .infinity, minHeight: 50)
            .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
            .disabled(isSigningIn)
            .overlay {
                if isSigningIn {
                    ProgressView().tint(.white)
                }
            }

            // A backend-side failure (invalid token, server error, ...) sets
            // `appState.lastError`, which `LoginView` already renders above
            // this button — showing it a second time here would just
            // duplicate the same message. Only a LOCAL, pre-network failure
            // (decode error, user cancelled) is shown here.
            if let localErrorMessage {
                Text(localErrorMessage).zenithBodySmStyle(color: .zenithError)
            }
        }
    }

    private func handle(_ result: Result<ASAuthorization, Error>) {
        switch result {
        case .success(let authorization):
            guard let credential = authorization.credential as? ASAuthorizationAppleIDCredential,
                  let tokenData = credential.identityToken,
                  let identityToken = String(data: tokenData, encoding: .utf8) else {
                localErrorMessage = "Apple-Anmeldung fehlgeschlagen. Bitte erneut versuchen."
                return
            }
            localErrorMessage = nil

            // Only present on the account's very first authorization with
            // this app — nil on every later sign-in, which is expected and
            // fine (AuthAPI.appleSignIn/the backend both treat it as optional).
            let fullName = credential.fullName.flatMap { components -> String? in
                let formatted = PersonNameComponentsFormatter.localizedString(from: components, style: .default)
                return formatted.isEmpty ? nil : formatted
            }

            isSigningIn = true
            Task {
                await appState.signInWithApple(identityToken: identityToken, fullName: fullName)
                isSigningIn = false
            }
        case .failure(let error):
            // The user cancelling the Apple Sign-In sheet also lands here
            // (ASAuthorizationError.canceled) — that's expected, not a
            // failure worth surfacing as an error.
            if (error as? ASAuthorizationError)?.code == .canceled {
                localErrorMessage = nil
            } else {
                localErrorMessage = error.localizedDescription
            }
        }
    }
}
