// ── Auth UI Kit ─────────────────────────────────────────────
// Reusable SwiftUI auth views for all ByteLyst iOS/macOS apps.
// BLLoginView, BLMfaChallengeView, BLPasskeyView, BLStepUpSheet.
// Themed via @Environment injection — always matches host product.

import SwiftUI
import os

#if canImport(AuthenticationServices)
import AuthenticationServices
#endif

private let logger = Logger(subsystem: "com.bytelyst.platform", category: "BLAuthUI")

// MARK: - Auth UI Configuration

/// Configuration for BLAuthUI views — passed via Environment.
public struct BLAuthUIConfig {
    public let productName: String
    public let accentColor: Color
    public let backgroundColor: Color
    public let textColor: Color
    public let secondaryTextColor: Color
    public let cardColor: Color
    public let enabledProviders: [BLAuthUIProvider]

    public init(
        productName: String = "ByteLyst",
        accentColor: Color = .blue,
        backgroundColor: Color = Color(.systemBackground),
        textColor: Color = .primary,
        secondaryTextColor: Color = .secondary,
        cardColor: Color = Color(.secondarySystemBackground),
        enabledProviders: [BLAuthUIProvider] = [.google, .apple]
    ) {
        self.productName = productName
        self.accentColor = accentColor
        self.backgroundColor = backgroundColor
        self.textColor = textColor
        self.secondaryTextColor = secondaryTextColor
        self.cardColor = cardColor
        self.enabledProviders = enabledProviders
    }
}

/// OAuth providers supported by BLAuthUI.
public enum BLAuthUIProvider: String, CaseIterable, Sendable {
    case google
    case microsoft
    case apple
}

// MARK: - Environment Key

private struct BLAuthUIConfigKey: EnvironmentKey {
    static let defaultValue = BLAuthUIConfig()
}

extension EnvironmentValues {
    public var blAuthUIConfig: BLAuthUIConfig {
        get { self[BLAuthUIConfigKey.self] }
        set { self[BLAuthUIConfigKey.self] = newValue }
    }
}

// MARK: - BLLoginView

/// Full login view with email/password + social buttons + passkey option.
/// Host product injects theme via `.environment(\.blAuthUIConfig, config)`.
public struct BLLoginView: View {
    @Environment(\.blAuthUIConfig) private var config

    @State private var email = ""
    @State private var password = ""
    @State private var isLoading = false
    @State private var errorMessage: String?

    /// Called with email + password when user taps Sign In.
    public var onLogin: (String, String) async throws -> Void
    /// Called with provider name when user taps a social button.
    public var onSocialLogin: (BLAuthUIProvider) -> Void
    /// Called when user taps "Use Passkey".
    public var onPasskeyLogin: (() -> Void)?
    /// Called when user taps "Forgot Password?".
    public var onForgotPassword: (() -> Void)?
    /// Called when user taps "Create Account".
    public var onCreateAccount: (() -> Void)?

    public init(
        onLogin: @escaping (String, String) async throws -> Void,
        onSocialLogin: @escaping (BLAuthUIProvider) -> Void,
        onPasskeyLogin: (() -> Void)? = nil,
        onForgotPassword: (() -> Void)? = nil,
        onCreateAccount: (() -> Void)? = nil
    ) {
        self.onLogin = onLogin
        self.onSocialLogin = onSocialLogin
        self.onPasskeyLogin = onPasskeyLogin
        self.onForgotPassword = onForgotPassword
        self.onCreateAccount = onCreateAccount
    }

    public var body: some View {
        ScrollView {
            VStack(spacing: 24) {
                // Header
                VStack(spacing: 8) {
                    Text("Sign in to \(config.productName)")
                        .font(.title2.bold())
                        .foregroundColor(config.textColor)
                    Text("Welcome back")
                        .font(.subheadline)
                        .foregroundColor(config.secondaryTextColor)
                }
                .padding(.top, 40)

                // Social Buttons
                if !config.enabledProviders.isEmpty {
                    VStack(spacing: 12) {
                        ForEach(config.enabledProviders, id: \.self) { provider in
                            socialButton(for: provider)
                        }
                    }

                    dividerRow
                }

                // Email / Password
                VStack(spacing: 16) {
                    TextField("Email", text: $email)
                        .textFieldStyle(.roundedBorder)
                        .textContentType(.emailAddress)
                        .keyboardType(.emailAddress)
                        .autocapitalization(.none)
                        .disableAutocorrection(true)

                    SecureField("Password", text: $password)
                        .textFieldStyle(.roundedBorder)
                        .textContentType(.password)
                }

                // Error
                if let errorMessage {
                    Text(errorMessage)
                        .font(.caption)
                        .foregroundColor(.red)
                        .multilineTextAlignment(.center)
                }

                // Sign In Button
                Button {
                    Task { await performLogin() }
                } label: {
                    HStack {
                        if isLoading {
                            ProgressView()
                                .tint(.white)
                        }
                        Text("Sign In")
                            .fontWeight(.semibold)
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 14)
                    .background(config.accentColor)
                    .foregroundColor(.white)
                    .cornerRadius(10)
                }
                .disabled(email.isEmpty || password.isEmpty || isLoading)

                // Passkey
                if let onPasskeyLogin {
                    Button {
                        logger.debug("Passkey login tapped")
                        onPasskeyLogin()
                    } label: {
                        HStack {
                            Image(systemName: "person.badge.key.fill")
                            Text("Sign in with Passkey")
                        }
                        .frame(maxWidth: .infinity)
                        .padding(.vertical, 14)
                        .background(config.cardColor)
                        .foregroundColor(config.textColor)
                        .cornerRadius(10)
                    }
                }

                // Forgot Password / Create Account
                VStack(spacing: 12) {
                    if let onForgotPassword {
                        Button("Forgot Password?") {
                            onForgotPassword()
                        }
                        .font(.subheadline)
                        .foregroundColor(config.accentColor)
                    }

                    if let onCreateAccount {
                        HStack {
                            Text("Don't have an account?")
                                .foregroundColor(config.secondaryTextColor)
                            Button("Create Account") {
                                onCreateAccount()
                            }
                            .foregroundColor(config.accentColor)
                        }
                        .font(.subheadline)
                    }
                }
            }
            .padding(.horizontal, 24)
        }
        .background(config.backgroundColor.ignoresSafeArea())
    }

    private func performLogin() async {
        isLoading = true
        errorMessage = nil
        do {
            logger.debug("Attempting email/password login for \(email)")
            try await onLogin(email, password)
        } catch let error as BLAuthError {
            // MFA required is not an error for the user — handled upstream
            logger.info("MFA required for \(email)")
            _ = error // Suppress unused warning
        } catch {
            logger.error("Login failed: \(error.localizedDescription)")
            errorMessage = error.localizedDescription
        }
        isLoading = false
    }

    @ViewBuilder
    private func socialButton(for provider: BLAuthUIProvider) -> some View {
        Button {
            logger.debug("Social login: \(provider.rawValue)")
            onSocialLogin(provider)
        } label: {
            HStack {
                providerIcon(for: provider)
                Text("Continue with \(providerDisplayName(provider))")
                    .fontWeight(.medium)
            }
            .frame(maxWidth: .infinity)
            .padding(.vertical, 14)
            .background(config.cardColor)
            .foregroundColor(config.textColor)
            .cornerRadius(10)
            .overlay(
                RoundedRectangle(cornerRadius: 10)
                    .stroke(Color.gray.opacity(0.3), lineWidth: 1)
            )
        }
    }

    @ViewBuilder
    private func providerIcon(for provider: BLAuthUIProvider) -> some View {
        switch provider {
        case .google:
            Image(systemName: "globe")
        case .microsoft:
            Image(systemName: "building.2")
        case .apple:
            Image(systemName: "applelogo")
        }
    }

    private func providerDisplayName(_ provider: BLAuthUIProvider) -> String {
        switch provider {
        case .google: return "Google"
        case .microsoft: return "Microsoft"
        case .apple: return "Apple"
        }
    }

    private var dividerRow: some View {
        HStack {
            Rectangle().fill(Color.gray.opacity(0.3)).frame(height: 1)
            Text("or")
                .font(.caption)
                .foregroundColor(config.secondaryTextColor)
            Rectangle().fill(Color.gray.opacity(0.3)).frame(height: 1)
        }
    }
}

// MARK: - BLMfaChallengeView

/// 6-digit TOTP code entry with countdown and recovery code fallback.
public struct BLMfaChallengeView: View {
    @Environment(\.blAuthUIConfig) private var config

    @State private var code = ""
    @State private var isLoading = false
    @State private var errorMessage: String?
    @State private var showRecovery = false

    public let challenge: BLMfaChallenge
    public var onVerify: (String, String, String) async throws -> Void
    public var onCancel: (() -> Void)?

    public init(
        challenge: BLMfaChallenge,
        onVerify: @escaping (String, String, String) async throws -> Void,
        onCancel: (() -> Void)? = nil
    ) {
        self.challenge = challenge
        self.onVerify = onVerify
        self.onCancel = onCancel
    }

    public var body: some View {
        VStack(spacing: 24) {
            // Header
            VStack(spacing: 8) {
                Image(systemName: "lock.shield.fill")
                    .font(.system(size: 48))
                    .foregroundColor(config.accentColor)

                Text("Two-Factor Authentication")
                    .font(.title3.bold())
                    .foregroundColor(config.textColor)

                Text(showRecovery
                    ? "Enter a recovery code"
                    : "Enter the 6-digit code from your authenticator app")
                    .font(.subheadline)
                    .foregroundColor(config.secondaryTextColor)
                    .multilineTextAlignment(.center)
            }

            // Code Input
            TextField(showRecovery ? "Recovery Code" : "000000", text: $code)
                .textFieldStyle(.roundedBorder)
                .keyboardType(showRecovery ? .default : .numberPad)
                .multilineTextAlignment(.center)
                .font(.title2.monospaced())
                .frame(maxWidth: 200)

            // Error
            if let errorMessage {
                Text(errorMessage)
                    .font(.caption)
                    .foregroundColor(.red)
            }

            // Verify Button
            Button {
                Task { await verify() }
            } label: {
                HStack {
                    if isLoading { ProgressView().tint(.white) }
                    Text("Verify")
                        .fontWeight(.semibold)
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 14)
                .background(config.accentColor)
                .foregroundColor(.white)
                .cornerRadius(10)
            }
            .disabled(code.isEmpty || isLoading)

            // Toggle recovery / cancel
            VStack(spacing: 12) {
                Button(showRecovery ? "Use authenticator code" : "Use recovery code") {
                    showRecovery.toggle()
                    code = ""
                    errorMessage = nil
                }
                .font(.subheadline)
                .foregroundColor(config.accentColor)

                if let onCancel {
                    Button("Cancel") { onCancel() }
                        .font(.subheadline)
                        .foregroundColor(config.secondaryTextColor)
                }
            }
        }
        .padding(24)
        .background(config.backgroundColor)
    }

    private func verify() async {
        isLoading = true
        errorMessage = nil
        let method = showRecovery ? "recovery" : "totp"
        do {
            logger.debug("Verifying MFA: method=\(method)")
            try await onVerify(challenge.mfaChallenge, code, method)
        } catch {
            logger.error("MFA verify failed: \(error.localizedDescription)")
            errorMessage = error.localizedDescription
            code = ""
        }
        isLoading = false
    }
}

// MARK: - BLPasskeyView

/// Passkey prompt with biometric hint text.
/// Triggers ASAuthorizationController for platform passkey authentication.
public struct BLPasskeyView: View {
    @Environment(\.blAuthUIConfig) private var config

    @State private var isLoading = false
    @State private var errorMessage: String?

    public var onAuthenticate: () async throws -> Void
    public var onCancel: (() -> Void)?

    public init(
        onAuthenticate: @escaping () async throws -> Void,
        onCancel: (() -> Void)? = nil
    ) {
        self.onAuthenticate = onAuthenticate
        self.onCancel = onCancel
    }

    public var body: some View {
        VStack(spacing: 24) {
            VStack(spacing: 12) {
                Image(systemName: "person.badge.key.fill")
                    .font(.system(size: 48))
                    .foregroundColor(config.accentColor)

                Text("Sign in with Passkey")
                    .font(.title3.bold())
                    .foregroundColor(config.textColor)

                Text("Use Face ID, Touch ID, or your security key to sign in")
                    .font(.subheadline)
                    .foregroundColor(config.secondaryTextColor)
                    .multilineTextAlignment(.center)
            }

            if let errorMessage {
                Text(errorMessage)
                    .font(.caption)
                    .foregroundColor(.red)
            }

            Button {
                Task { await authenticate() }
            } label: {
                HStack {
                    if isLoading { ProgressView().tint(.white) }
                    Image(systemName: "faceid")
                    Text("Continue")
                        .fontWeight(.semibold)
                }
                .frame(maxWidth: .infinity)
                .padding(.vertical, 14)
                .background(config.accentColor)
                .foregroundColor(.white)
                .cornerRadius(10)
            }
            .disabled(isLoading)

            if let onCancel {
                Button("Use another method") { onCancel() }
                    .font(.subheadline)
                    .foregroundColor(config.accentColor)
            }
        }
        .padding(24)
        .background(config.backgroundColor)
    }

    private func authenticate() async {
        isLoading = true
        errorMessage = nil
        do {
            logger.debug("Starting passkey authentication")
            try await onAuthenticate()
        } catch {
            logger.error("Passkey auth failed: \(error.localizedDescription)")
            errorMessage = error.localizedDescription
        }
        isLoading = false
    }
}

// MARK: - BLStepUpSheet

/// Re-authentication sheet for sensitive operations.
/// Supports password re-entry or biometric confirmation.
public struct BLStepUpSheet: View {
    @Environment(\.blAuthUIConfig) private var config
    @Environment(\.dismiss) private var dismiss

    @State private var password = ""
    @State private var isLoading = false
    @State private var errorMessage: String?

    public let reason: String
    public var onStepUp: (String, String) async throws -> String
    public var onComplete: (String) -> Void

    public init(
        reason: String = "This action requires re-authentication",
        onStepUp: @escaping (String, String) async throws -> String,
        onComplete: @escaping (String) -> Void
    ) {
        self.reason = reason
        self.onStepUp = onStepUp
        self.onComplete = onComplete
    }

    public var body: some View {
        NavigationStack {
            VStack(spacing: 24) {
                VStack(spacing: 8) {
                    Image(systemName: "lock.fill")
                        .font(.system(size: 36))
                        .foregroundColor(config.accentColor)

                    Text("Confirm Your Identity")
                        .font(.title3.bold())
                        .foregroundColor(config.textColor)

                    Text(reason)
                        .font(.subheadline)
                        .foregroundColor(config.secondaryTextColor)
                        .multilineTextAlignment(.center)
                }

                SecureField("Password", text: $password)
                    .textFieldStyle(.roundedBorder)
                    .textContentType(.password)

                if let errorMessage {
                    Text(errorMessage)
                        .font(.caption)
                        .foregroundColor(.red)
                }

                #if canImport(LocalAuthentication)
                Button {
                    Task { await biometricStepUp() }
                } label: {
                    HStack {
                        Image(systemName: "faceid")
                        Text("Use Biometrics")
                            .fontWeight(.medium)
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 14)
                    .background(config.cardColor)
                    .foregroundColor(config.textColor)
                    .cornerRadius(10)
                }
                #endif

                Button {
                    Task { await passwordStepUp() }
                } label: {
                    HStack {
                        if isLoading { ProgressView().tint(.white) }
                        Text("Confirm")
                            .fontWeight(.semibold)
                    }
                    .frame(maxWidth: .infinity)
                    .padding(.vertical, 14)
                    .background(config.accentColor)
                    .foregroundColor(.white)
                    .cornerRadius(10)
                }
                .disabled(password.isEmpty || isLoading)

                Spacer()
            }
            .padding(24)
            .background(config.backgroundColor)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button("Cancel") { dismiss() }
                }
            }
        }
    }

    private func passwordStepUp() async {
        isLoading = true
        errorMessage = nil
        do {
            logger.debug("Step-up: password method")
            let token = try await onStepUp("password", password)
            onComplete(token)
            dismiss()
        } catch {
            logger.error("Step-up failed: \(error.localizedDescription)")
            errorMessage = error.localizedDescription
        }
        isLoading = false
    }

    #if canImport(LocalAuthentication)
    private func biometricStepUp() async {
        let success = await BLBiometricAuth.authenticate(reason: "Confirm your identity")
        if success {
            isLoading = true
            errorMessage = nil
            do {
                logger.debug("Step-up: biometric method")
                let token = try await onStepUp("biometric", "biometric_verified")
                onComplete(token)
                dismiss()
            } catch {
                logger.error("Biometric step-up failed: \(error.localizedDescription)")
                errorMessage = error.localizedDescription
            }
            isLoading = false
        } else {
            errorMessage = "Biometric authentication failed"
        }
    }
    #endif
}

// ── BLDeviceListView ────────────────────────────────────────

/// Device management view — list trusted/remembered devices, revoke trust.
/// Mirrors the Kotlin `BLDeviceListScreen` for platform parity.
///
/// - Parameters:
///   - devices: List of devices from `BLAuthClient.listDevices()`.
///   - onRevokeDevice: Called with device ID when user revokes a device.
///   - onRevokeAll: Called when user revokes all devices. `nil` hides the button.
///   - isLoading: Whether data is loading.
public struct BLDeviceListView: View {
    public let devices: [BLDevice]
    public let onRevokeDevice: (String) -> Void
    public var onRevokeAll: (() -> Void)?
    public var isLoading: Bool = false

    public init(
        devices: [BLDevice],
        onRevokeDevice: @escaping (String) -> Void,
        onRevokeAll: (() -> Void)? = nil,
        isLoading: Bool = false
    ) {
        self.devices = devices
        self.onRevokeDevice = onRevokeDevice
        self.onRevokeAll = onRevokeAll
        self.isLoading = isLoading
    }

    public var body: some View {
        ScrollView {
            VStack(alignment: .leading, spacing: 16) {
                HStack {
                    Text("Your Devices")
                        .font(.title2.bold())
                    Spacer()
                    if let onRevokeAll, !devices.isEmpty {
                        Button(role: .destructive, action: onRevokeAll) {
                            Text("Revoke All")
                        }
                    }
                }

                if isLoading {
                    HStack {
                        Spacer()
                        ProgressView()
                        Spacer()
                    }
                    .padding(.vertical, 32)
                } else if devices.isEmpty {
                    Text("No devices found")
                        .foregroundStyle(.secondary)
                        .padding(.vertical, 16)
                } else {
                    ForEach(devices, id: \.id) { device in
                        DeviceCardView(device: device) {
                            onRevokeDevice(device.id)
                        }
                    }
                }
            }
            .padding()
        }
    }
}

/// Individual device card within BLDeviceListView.
private struct DeviceCardView: View {
    let device: BLDevice
    let onRevoke: () -> Void

    private var platformIcon: String {
        switch device.platform {
        case "ios": return "iphone"
        case "android": return "apps.iphone"
        case "macos": return "laptopcomputer"
        case "windows", "linux": return "desktopcomputer"
        default: return "display"
        }
    }

    private var trustColor: Color {
        switch device.trustLevel {
        case "trusted": return .blue
        case "remembered": return .green
        default: return .secondary
        }
    }

    var body: some View {
        HStack(spacing: 12) {
            Image(systemName: platformIcon)
                .font(.title2)
                .foregroundStyle(trustColor)
                .frame(width: 32)

            VStack(alignment: .leading, spacing: 2) {
                Text(device.name)
                    .font(.body)
                Text("\(device.trustLevel) · \(device.platform)")
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            Spacer()

            if device.trustLevel == "trusted" || device.trustLevel == "remembered" {
                Button(role: .destructive, action: onRevoke) {
                    Image(systemName: "xmark.circle.fill")
                        .foregroundStyle(.red)
                }
                .buttonStyle(.plain)
            }
        }
        .padding()
        .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12))
    }
}
