import BureauSMV
import Foundation
import Network

/**
 * React Native bridge module for Bureau SDK
 * Handles authentication
 */
@objc(BureauOtlModule)
class BureauOtlModule: NSObject {
    
    /// Debug logging flag - disabled by default
    static var debugEnabled = false
    
    /// Environment constants
    private enum Environment {
        static let production = "production"
        static let sandbox = "sandbox"
    }
    
    /// Result keys
    private enum ResultKey {
        static let status = "status"
        static let message = "message"
    }
    
    /// Status case names
    private enum StatusCase {
        static let networkAndOperatorMismatch = "networkAndOperatorMismatch"
        static let authFailure = "authFailure"
        static let networkNotSupported = "networkNotSupported"
        static let operatorNotSupported = "operatorNotSupported"
        static let operatorAndNetworkNotSupported = "operatorAndNetworkNotSupported"
        static let awaitingProviderAck = "awaitingProviderAck"
        static let authValidationError = "authValidationError"
        static let duplicateCorrelationId = "duplicateCorrelationId"
        static let integrationFailure = "integrationFailure"
        static let authStateExpired = "authStateExpired"
        static let unauthorized = "unauthorized"
        static let countryNotSupported = "countryNotSupported"
        static let rateLimitExceeded = "rateLimitExceeded"
        static let internalServerError = "internalServerError"
        static let networkUnavailable = "networkUnavailable"
        static let wifiDetectedAndNoDataNetwork = "wifiDetectedAndNoDataNetwork"
        static let completed = "completed"
        static let timeout = "timeout"
        static let unknown = "unknown"
    }
    
    /// Dictionary mapping SDK enum case patterns to status strings for efficient lookup
    /// This allows O(1) lookup instead of linear switch statement search
    private static let enumCaseToStatusMap: [String: String] = [
        "networkAndOperatorMismatch": StatusCase.networkAndOperatorMismatch,
        "authFailure": StatusCase.authFailure,
        "networkNotSupported": StatusCase.networkNotSupported,
        "operatorNotSupported": StatusCase.operatorNotSupported,
        "operatorAndNetworkNotSupported": StatusCase.operatorAndNetworkNotSupported,
        "awaitingProviderAck": StatusCase.awaitingProviderAck,
        "authValidationError": StatusCase.authValidationError,
        "duplicateCorrelationId": StatusCase.duplicateCorrelationId,
        "integrationFailure": StatusCase.integrationFailure,
        "authStateExpired": StatusCase.authStateExpired,
        "unauthorized": StatusCase.unauthorized,
        "countryNotSupported": StatusCase.countryNotSupported,
        "rateLimitExceeded": StatusCase.rateLimitExceeded,
        "internalServerError": StatusCase.internalServerError,
        "networkUnavailable": StatusCase.networkUnavailable,
        "wifiDetectedAndNoDataNetwork": StatusCase.wifiDetectedAndNoDataNetwork,
        "completed": StatusCase.completed,
        "unknown": StatusCase.unknown
    ]
    
    /// Timeout conversion constant (milliseconds to seconds)
    private enum TimeoutConversion {
        static let millisecondsPerSecond = 1000
    }
    
    /// Log tag for debug logging
    private enum LogTag {
        static let tag = "BureauSDK"
    }
    
    /// Log levels
    private enum LogLevel: String {
        case debug = "DEBUG"
        case info = "INFO"
        case error = "ERROR"
    }

    override init() {
        super.init()
    }
    
    /**
     * Enables or disables debug logging
     *
     * @param enabled - Whether to enable debug logging
     */
    @objc(setDebugEnabled:)
    func setDebugEnabled(_ enabled: Bool) {
        Self.debugEnabled = enabled
        let logMessage = "[\(LogTag.tag)] [\(LogLevel.info.rawValue)] Debug logging \(enabled ? "enabled" : "disabled")"
        NSLog("%@", logMessage)
    }
    
    /**
     * Retrieves all native SDK logs from LogManager
     * Returns logs in a format compatible with React Native LogEntry structure
     *
     * @param resolve - Promise resolver for array of log entries
     * @param reject - Promise rejecter for errors
     */
    @objc(getNativeLogs:withRejecter:)
    func getNativeLogs(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
        // Get logs from LogManager (iOS SDK uses LogManager.shared.allLogs())
        // Note: LogManager is not available in BureauSMV framework
        // Return empty array for now - this feature may be available in future SDK versions
        let logStrings: [String] = []
        
        // Parse log strings and convert to React Native format
        var logArray: [[String: Any]] = []
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        
        for (index, logString) in logStrings.enumerated() {
            // Parse log string format: [timestamp] [LEVEL] message
            // Example: [2025-01-15 14:23:45.123] [INFO] Authentication started
            var timestamp: Double = Date().timeIntervalSince1970 * 1000
            var level = "INFO"
            var message = logString
            
            // Try to parse timestamp and level from log string
            if let timestampMatch = logString.range(of: #"\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3})\]"#, options: .regularExpression) {
                let timestampString = String(logString[timestampMatch])
                    .replacingOccurrences(of: "[", with: "")
                    .replacingOccurrences(of: "]", with: "")
                
                if let date = dateFormatter.date(from: timestampString) {
                    timestamp = date.timeIntervalSince1970 * 1000
                }
            }
            
            // Extract log level
            if let levelMatch = logString.range(of: #"\[(DEBUG|INFO|WARNING|ERROR|VERBOSE)\]"#, options: .regularExpression) {
                let levelString = String(logString[levelMatch])
                    .replacingOccurrences(of: "[", with: "")
                    .replacingOccurrences(of: "]", with: "")
                
                // Map iOS log levels to React Native log levels
                switch levelString.uppercased() {
                case "DEBUG", "VERBOSE":
                    level = "DEBUG"
                case "INFO", "WARNING":
                    level = "INFO"
                case "ERROR":
                    level = "ERROR"
                default:
                    level = "INFO"
                }
                
                // Remove timestamp and level from message
                message = logString
                    .replacingOccurrences(of: #"\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}\]"#, with: "", options: .regularExpression)
                    .replacingOccurrences(of: #"\[(DEBUG|INFO|WARNING|ERROR|VERBOSE)\]"#, with: "", options: .regularExpression)
                    .trimmingCharacters(in: .whitespaces)
            }
            
            let logEntry: [String: Any] = [
                "id": "\(Int(timestamp))-\(index)",
                "timestamp": timestamp,
                "level": level,
                "message": message.isEmpty ? logString : message
            ]
            logArray.append(logEntry)
        }
        
        resolve(logArray)
    }
    
    /**
     * Masks sensitive data for logging (shows first 4 and last 4 characters)
     */
    private static func maskSensitiveData(_ value: String) -> String {
        if value.count <= 8 {
            return "***"
        }
        let start = value.prefix(4)
        let end = value.suffix(4)
        return "\(start)...\(end)"
    }
    
    /**
     * Logs debug message to NSLog (appears in Xcode console and device logs)
     */
    private static func logDebug(_ message: String) {
        guard debugEnabled else { return }
        let logMessage = "[\(LogTag.tag)] [\(LogLevel.debug.rawValue)] \(message)"
        NSLog("%@", logMessage)
    }
    
    /**
     * Logs info message to NSLog (appears in Xcode console and device logs)
     */
    private static func logInfo(_ message: String) {
        guard debugEnabled else { return }
        let logMessage = "[\(LogTag.tag)] [\(LogLevel.info.rawValue)] \(message)"
        NSLog("%@", logMessage)
    }
    
    /**
     * Logs error message to NSLog (appears in Xcode console and device logs)
     */
    private static func logError(_ message: String) {
        guard debugEnabled else { return }
        let logMessage = "[\(LogTag.tag)] [\(LogLevel.error.rawValue)] \(message)"
        NSLog("%@", logMessage)
    }
    
    /**
     * Efficiently extracts the case identifier from SDK AuthenticationStatus enum
     * The new BureauSMV framework has all detailed enum cases with associated values
     *
     * @param sdkStatus - The AuthenticationStatus from BureauSMV SDK
     * @return Case identifier string (e.g., "completed", "networkUnavailable")
     */
    private static func extractCaseIdentifier(_ sdkStatus: BureauSMV.AuthenticationStatus) -> String {
        // Use String(describing:) to extract the enum case name
        return String(describing: sdkStatus)
    }
    
    /**
     * Network state information for error mapping
     */
    private struct NetworkState {
        let isWifiAvailable: Bool
        let isCellularAvailable: Bool
        
        var hasNoNetwork: Bool {
            return !isWifiAvailable && !isCellularAvailable
        }
        
        var hasWifiOnly: Bool {
            return isWifiAvailable && !isCellularAvailable
        }
    }
    
    /**
     * Checks current network state synchronously
     * Uses Network framework to detect WiFi and Cellular availability
     *
     * @return NetworkState with WiFi and Cellular availability flags
     */
    private static func checkNetworkState() -> NetworkState {
        let semaphore = DispatchSemaphore(value: 0)
        var wifiAvailable = false
        var cellularAvailable = false
        
        let monitor = NWPathMonitor()
        let queue = DispatchQueue.global(qos: .utility)
        
        monitor.pathUpdateHandler = { path in
            wifiAvailable = path.usesInterfaceType(.wifi)
            cellularAvailable = path.usesInterfaceType(.cellular)
            semaphore.signal()
        }
        
        monitor.start(queue: queue)
        _ = semaphore.wait(timeout: .now() + 2.0) // 2 second timeout for network check
        monitor.cancel()
        
        return NetworkState(isWifiAvailable: wifiAvailable, isCellularAvailable: cellularAvailable)
    }
    
    /**
     * Extracts message from AuthenticationStatus enum using reflection
     * The framework may have a message property even if enum cases are simplified
     * Also tries to extract associated values from the enum
     *
     * @param sdkStatus - The AuthenticationStatus from BureauSMV SDK
     * @return Optional message string if available
     */
    private static func extractMessage(from sdkStatus: BureauSMV.AuthenticationStatus) -> String? {
        // Try to access message property using reflection
        let mirror = Mirror(reflecting: sdkStatus)
        
        // Check if there's a message property (the full SDK has this)
        for child in mirror.children {
            if child.label == "message" {
                if let message = child.value as? String {
                    return message
                }
            }
        }
        
        // Try to extract associated value directly from enum case
        // The enum cases have associated String values: .networkAndOperatorMismatch(String)
        for child in mirror.children {
            if child.label == nil || child.label == "some" {
                if let associatedValue = child.value as? String {
                    return associatedValue
                }
                // Try nested extraction for optionals
                let nestedMirror = Mirror(reflecting: child.value)
                for nestedChild in nestedMirror.children {
                    if let stringValue = nestedChild.value as? String {
                        return stringValue
                    }
                }
            }
        }
        
        // Try to extract from the enum's raw value or description
        let description = String(describing: sdkStatus)
        // If description contains a message pattern, try to extract it
        // Format might be: "networkAndOperatorMismatch("message")" or similar
        if let startRange = description.range(of: "("),
           let endRange = description.range(of: ")", range: startRange.upperBound..<description.endIndex) {
            let message = String(description[startRange.upperBound..<endRange.lowerBound])
            if !message.isEmpty && message.count > 5 { // Reasonable message length
                return message.trimmingCharacters(in: CharacterSet(charactersIn: "\""))
            }
        }
        
        return nil
    }
    
    /**
     * Infers error status from message content
     * Uses pattern matching on message strings to determine the actual error type
     * Based on Bureau API error messages and stakeholder feedback
     *
     * @param message - The error message from SDK
     * @return Inferred status case name, or nil if cannot be determined
     */
    private static func inferStatusFromMessage(_ message: String) -> String? {
        let lowercased = message.lowercased()
        
        // Pattern matching for different error types based on Bureau API error messages
        // Priority order matters - more specific patterns first
        
        // 1. Operator not supported (MSISDN doesn't belong to supported provider)
        // Examples: "User's MSISDN doesn't belong to any supported provider", "msisdn not supported"
        if (lowercased.contains("msisdn") && (lowercased.contains("not supported") || lowercased.contains("doesn't belong"))) ||
           lowercased.contains("msisdn not supported") {
            return StatusCase.operatorNotSupported
        }
        
        // 2. Network and operator mismatch (Providers failed to verify)
        // Examples: "User verification failed. Providers failed to verify", "network and operator mismatch"
        if (lowercased.contains("network") && lowercased.contains("operator") && (lowercased.contains("mismatch") || lowercased.contains("failed to verify"))) ||
           lowercased.contains("networkandoperatormismatch") ||
           lowercased.contains("network and operator mismatch") {
            return StatusCase.networkAndOperatorMismatch
        }
        
        // 3. Operator and network both not supported
        if lowercased.contains("network") && lowercased.contains("operator") && lowercased.contains("not supported") &&
           (lowercased.contains("ip") || lowercased.contains("msisdn")) {
            return StatusCase.operatorAndNetworkNotSupported
        }
        
        // 4. Network not supported (IP doesn't belong to supported provider)
        if lowercased.contains("ip") && (lowercased.contains("doesn't belong") || lowercased.contains("not supported")) {
            return StatusCase.networkNotSupported
        }
        
        // 5. WiFi detected but no data network
        if (lowercased.contains("wifi") && (lowercased.contains("no data") || lowercased.contains("no data network"))) ||
           lowercased.contains("wifi detected and no data network") ||
           lowercased.contains("wifi and no data network") {
            return StatusCase.wifiDetectedAndNoDataNetwork
        }
        
        // 6. Network unavailable
        if lowercased.contains("network unavailable") {
            return StatusCase.networkUnavailable
        }
        
        return nil
    }
    
    /**
     * Extracts the enum case name from AuthenticationStatus using string representation
     * Tries to identify the actual error type from the enum's string description
     *
     * @param sdkStatus - The AuthenticationStatus from BureauSMV SDK
     * @return Optional status case name if can be inferred
     */
    private static func extractStatusCaseName(from sdkStatus: BureauSMV.AuthenticationStatus) -> String? {
        let statusString = String(describing: sdkStatus)
        let lowercased = statusString.lowercased()
        
        // Try to match enum case patterns in the string representation
        // The framework might include the case name in the description
        if lowercased.contains("networkandoperatormismatch") || lowercased.contains("network_and_operator_mismatch") {
            return StatusCase.networkAndOperatorMismatch
        }
        if lowercased.contains("operatornotsupported") || lowercased.contains("operator_not_supported") {
            return StatusCase.operatorNotSupported
        }
        if lowercased.contains("networknotsupported") || lowercased.contains("network_not_supported") {
            return StatusCase.networkNotSupported
        }
        if lowercased.contains("wifidetectedandnodatanetwork") || lowercased.contains("wifi_detected_and_no_data_network") {
            return StatusCase.wifiDetectedAndNoDataNetwork
        }
        
        return nil
    }
    
    /**
     * Maps SDK AuthenticationStatus to React Native status, message, and log level
     * Enhanced to handle network state detection and message parsing for better error mapping
     *
     * @param sdkStatus - The AuthenticationStatus from BureauSMV SDK
     * @param networkState - Current network state (optional, checked if needed)
     * @return Tuple containing (status: String, message: String, logLevel: LogLevel)
     */
    private static func mapSDKStatusToReactNativeStatus(
        _ sdkStatus: BureauSMV.AuthenticationStatus,
        networkState: NetworkState? = nil
    ) -> (status: String, message: String, logLevel: LogLevel) {
        // Map all detailed enum cases directly - the framework now exposes all cases with associated String values
        // When updating xcframework from IOS-SDK develop: if the SDK adds .timeout(String), add: case .timeout(let message): return (StatusCase.timeout, message, .error)
        switch sdkStatus {
        // Success case
        case .completed(let message):
            return (StatusCase.completed, message, .info)
            
        case .timeout(let message):
            return (StatusCase.timeout, message, .error)
        
        // Network-related errors
        case .networkUnavailable(let message):
            return (StatusCase.networkUnavailable, message, .error)
        case .wifiDetectedAndNoDataNetwork(let message):
            return (StatusCase.wifiDetectedAndNoDataNetwork, message, .error)
        case .networkNotSupported(let message):
            return (StatusCase.networkNotSupported, message, .error)
        
        // Operator-related errors
        case .operatorNotSupported(let message):
            return (StatusCase.operatorNotSupported, message, .error)
        case .networkAndOperatorMismatch:
            // User-facing message; do not surface raw API text ("User verification Failed. Providers failed to verify")
            return (StatusCase.networkAndOperatorMismatch, Self.getDefaultMessage(for: StatusCase.networkAndOperatorMismatch), .info)
        case .operatorAndNetworkNotSupported(let message):
            return (StatusCase.operatorAndNetworkNotSupported, message, .error)
        
        // Authentication errors
        case .authFailure(let message):
            return (StatusCase.authFailure, message, .error)
        case .authValidationError(let message):
            return (StatusCase.authValidationError, message, .error)
        case .authStateExpired(let message):
            return (StatusCase.authStateExpired, message, .error)
        case .unauthorized(let message):
            return (StatusCase.unauthorized, message, .error)
        
        // Request/response errors
        case .awaitingProviderAck(let message):
            return (StatusCase.awaitingProviderAck, message, .info)
        case .duplicateCorrelationId(let message):
            return (StatusCase.duplicateCorrelationId, message, .error)
        case .integrationFailure(let message):
            return (StatusCase.integrationFailure, message, .error)
        
        // Server errors
        case .rateLimitExceeded(let message):
            return (StatusCase.rateLimitExceeded, message, .error)
        case .internalServerError(let message):
            return (StatusCase.internalServerError, message, .error)
        case .countryNotSupported(let message):
            return (StatusCase.countryNotSupported, message, .error)
        
        // Fallback
        case .unknown(let message):
            // For unknown errors, try to infer from message or network state as fallback
            if let inferredStatus = Self.inferStatusFromMessage(message) {
                return (inferredStatus, message, inferredStatus == StatusCase.networkAndOperatorMismatch ? .info : .error)
            }
            
            // Check network state as last resort
            let network = networkState ?? checkNetworkState()
            if network.hasNoNetwork {
                return (StatusCase.networkUnavailable, "Network unavailable", .error)
            } else if network.hasWifiOnly {
                return (StatusCase.wifiDetectedAndNoDataNetwork, "WiFi and no data network", .error)
            }
            
            return (StatusCase.unknown, message, .error)
        }
    }
    
    /**
     * Detects if a timeout occurred based on SDK status and elapsed time
     *
     * @param sdkStatus - The AuthenticationStatus from BureauSMV SDK
     * @param startTime - Date when authentication started
     * @param timeout - Configured timeout duration in milliseconds
     * @return True if timeout is detected, false otherwise
     */
    private static func detectTimeout(sdkStatus: BureauSMV.AuthenticationStatus, startTime: Date, timeout: Int) -> Bool {
        let elapsedTimeMs = Date().timeIntervalSince(startTime) * 1000
        // Check if status is unknown (timeout candidate) without network state for timeout detection
        let isUnknown = String(describing: sdkStatus).contains("unknown")
        let isTimeoutCandidate = isUnknown
        
        return isTimeoutCandidate && elapsedTimeMs >= Double(timeout)
    }
    
    /**
     * Creates a standardized timeout error log message with context
     *
     * @param clientId - Bureau client ID (will be masked)
     * @param sessionId - Session ID (will be masked)
     * @param timeout - Configured timeout duration in milliseconds
     * @param elapsedTime - Actual elapsed time in milliseconds
     * @param env - Environment string
     * @return Formatted log message string
     */
    private static func createTimeoutLogContext(
        clientId: String,
        sessionId: String,
        timeout: Int,
        elapsedTime: TimeInterval,
        env: String
    ) -> String {
        // Guard against division by zero
        guard timeout > 0 else {
            let elapsedTimeMs = Int(elapsedTime * 1000)
            return "Timeout error detected - duration: \(timeout)ms (invalid), elapsed: \(elapsedTimeMs)ms, " +
                "sessionId: \(maskSensitiveData(sessionId)), clientId: \(maskSensitiveData(clientId)), env: \(env), platform: iOS"
        }
        
        let elapsedTimeMs = Int(elapsedTime * 1000)
        let percentage = String(
            format: "%.1f",
            Double((elapsedTimeMs * 100) / timeout)
        )
        return "Timeout error detected - duration: \(timeout)ms, elapsed: \(elapsedTimeMs)ms (\(percentage)% of timeout), " +
            "sessionId: \(maskSensitiveData(sessionId)), clientId: \(maskSensitiveData(clientId)), env: \(env), platform: iOS"
    }
    
    /**
     * Gets default message for a status case
     *
     * @param statusCase - The status case name
     * @return Default message string
     */
    private static func getDefaultMessage(for statusCase: String) -> String {
        switch statusCase {
        case StatusCase.networkAndOperatorMismatch:
            return "Provider of user's IP and MSISDN do not match"
        case StatusCase.operatorNotSupported:
            return "msisdn not supported"
        case StatusCase.networkNotSupported:
            return "Network not supported"
        case StatusCase.operatorAndNetworkNotSupported:
            return "Operator and network not supported"
        case StatusCase.wifiDetectedAndNoDataNetwork:
            return "WiFi and no data network"
        case StatusCase.networkUnavailable:
            return "Network unavailable"
        case StatusCase.completed:
            return "Authentication completed successfully"
        default:
            return "Unknown authentication failure"
        }
    }
    
    /**
     * Logs authentication result with appropriate log level
     *
     * @param status - The authentication status case name
     * @param message - The authentication status message
     * @param logLevel - The log level to use
     */
    private static func logAuthenticationResult(status: String, message: String, logLevel: LogLevel) {
        guard debugEnabled else { return }
        let logMessage = "Authentication status: \(status) - \(message)"
        let formattedMessage = "[\(LogTag.tag)] [\(logLevel.rawValue)] \(logMessage)"
        NSLog("%@", formattedMessage)
    }

    /**
     * Authenticates a user using Bureau's One-Tap Login
     *
     * @param clientId - Bureau client ID
     * @param sessionId - Unique session/correlation ID
     * @param msisdn - Mobile number with country code
     * @param env - Environment: "production" or "sandbox"
     * @param timeout - Timeout in milliseconds
     * @param allowedCountryCodes - Array of allowed country codes (e.g., ["91", "1"]). Note: iOS SDK doesn't use this parameter, but it's included for API consistency with React Native layer
     * @param pspCallbacks - PSP callback keys for BureauSMV Initiate (1.2.1+)
     * @param resolve - Promise resolver for authentication result
     * @param reject - Promise rejecter (not used, errors are resolved in result)
     */
    @objc(authenticate:withSessionId:withMsisdn:withEnv:withTimeout:withAllowedCountryCodes:withPspCallbacks:withResolver:withRejecter:)
    func authenticate(clientId:String, sessionId:String, msisdn:String, env:NSString, timeout:NSInteger, allowedCountryCodes:NSArray?, pspCallbacks:NSArray?, resolve:@escaping RCTPromiseResolveBlock, reject:RCTPromiseRejectBlock){
        let startTime = Date()
        
        // Step 1: Entry point logging
        let maskedClientId = Self.maskSensitiveData(clientId)
        let maskedSessionId = Self.maskSensitiveData(sessionId)
        let maskedMsisdn = Self.maskSensitiveData(msisdn)
        let timeoutInSeconds = timeout / TimeoutConversion.millisecondsPerSecond
        
        // Convert allowedCountryCodes array to string for logging
        let countryCodesString: String
        if let codes = allowedCountryCodes as? [String], !codes.isEmpty {
            countryCodesString = codes.joined(separator: ", ")
        } else {
            countryCodesString = "none"
        }
        
        let pspCallbacksForSdk = (pspCallbacks as? [String])?
            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter { !$0.isEmpty } ?? []

        // Log entry point - this will be captured by LogManager via native SDK
        // Note: allowedCountryCodes is validated in React Native layer; iOS SDK doesn't use this parameter
        Self.logDebug("authenticate: Entry - clientId: \(maskedClientId), sessionId: \(maskedSessionId), msisdn: \(maskedMsisdn), env: \(env), timeout: \(timeoutInSeconds)s, allowedCountryCodes: [\(countryCodesString)], pspCallbacksCount: \(pspCallbacksForSdk.count)")
        
        // Step 2: Determine environment mode (case-insensitive to match JS/README usage)
        var mode = BureauSMV.Mode.sandbox
        let envString = (env as String).lowercased()
        if envString == Environment.production {
            mode = BureauSMV.Mode.production
        }
        let modeString = mode == BureauSMV.Mode.production ? "production" : "sandbox"
        Self.logDebug("authenticate: Environment mode selected: \(modeString) (from input: '\(env)')")
        
        // Step 3: Convert timeout from milliseconds to seconds
        Self.logDebug("authenticate: Timeout converted: \(timeout)ms -> \(timeoutInSeconds)s")
        
        // Step 4: Build BureauAuth instance
        // Enable debug logging in native SDK if debug is enabled in React Native
        Self.logDebug("authenticate: Building BureauAuth instance with environment: \(modeString), timeout: \(timeoutInSeconds)s")
        var builder = BureauAuth.Builder()
            .setClientId(clientId: clientId)
            .setMode(mode: mode)
            .setTimeout(timeoutinSeconds: Int(timeoutInSeconds))
        if Self.debugEnabled {
            builder = builder.setLogLevel(.debug)
        }
        let bureauObj = builder.build()
        Self.logDebug("authenticate: BureauAuth instance created successfully")
        
        // Step 5: Check network state before making authentication call
        // This helps us provide better error messages when SDK returns generic errors
        Self.logDebug("authenticate: Checking network state before SDK call")
        let networkStateBefore = Self.checkNetworkState()
        Self.logDebug("authenticate: Network state - WiFi: \(networkStateBefore.isWifiAvailable), Cellular: \(networkStateBefore.isCellularAvailable)")
        
        // Step 6: Make authentication call
        // Note: The native SDK (makeAuthCall) will automatically log all step-by-step details
        // including network checks, route selection, request/response, etc.
        Self.logDebug("authenticate: Initiating authentication call to SDK - correlationId: \(maskedSessionId), mobileNumber: \(maskedMsisdn)")
        
        bureauObj.makeAuthCall(
            mobile: String(msisdn),
            correlationId: sessionId,
            pspCallbacks: pspCallbacksForSdk
        ) { sdkStatus in
            DispatchQueue.main.async {
        
        let elapsedTime = Date().timeIntervalSince(startTime)
        let elapsedSeconds = String(format: "%.4f", elapsedTime)
        
        // Step 7: Check network state after SDK call (in case it changed)
        let networkStateAfter = Self.checkNetworkState()
        
        // Step 8: Log response received
        Self.logDebug("authenticate: SDK response received - status: \(String(describing: sdkStatus)), correlationId: \(maskedSessionId)")
        Self.logDebug("authenticate: Network state after call - WiFi: \(networkStateAfter.isWifiAvailable), Cellular: \(networkStateAfter.isCellularAvailable)")
        
        // Step 9: Map SDK status to React Native status using helper with network state context
        // Use the network state to provide better error mapping for unknown/onDifferentNetwork cases
        let mapped = Self.mapSDKStatusToReactNativeStatus(sdkStatus, networkState: networkStateAfter)
        
        // Detect timeout scenarios (normalize to whole seconds like SDK and convert back to ms)
        // Note: The new framework's .unknown case might indicate timeout
        let normalizedTimeoutSeconds = max(timeout / TimeoutConversion.millisecondsPerSecond, 1)
        let normalizedTimeoutMs = normalizedTimeoutSeconds * TimeoutConversion.millisecondsPerSecond
        let isTimeout = Self.detectTimeout(sdkStatus: sdkStatus, startTime: startTime, timeout: normalizedTimeoutMs)
        let finalStatus = isTimeout ? StatusCase.timeout : mapped.status
        let finalMessage = isTimeout ? "Authentication timeout after \(timeout)ms" : mapped.message
        
        // Step 10: Log timeout error with detailed context if timeout detected
        if isTimeout {
            let safeTimeout = max(timeout, 1)
            let timeoutContext = Self.createTimeoutLogContext(
                clientId: clientId,
                sessionId: sessionId,
                timeout: safeTimeout,
                elapsedTime: elapsedTime,
                env: envString
            )
            Self.logError("authenticate: TIMEOUT detected - \(timeoutContext)")
        }
        
        // Step 11: Log final authentication result
        if finalStatus == StatusCase.completed {
            Self.logInfo("authenticate: Authentication successful - Status: \(finalStatus), Message: \(finalMessage), Correlation Id: \(maskedSessionId), Duration: \(elapsedSeconds)s")
        } else {
            Self.logError("authenticate: Authentication failed - Status: \(finalStatus), Message: \(finalMessage), Correlation Id: \(maskedSessionId), Duration: \(elapsedSeconds)s")
        }
        
        // Log authentication result with appropriate level
        let finalLogLevel = isTimeout ? LogLevel.error : mapped.logLevel
        Self.logAuthenticationResult(status: finalStatus, message: finalMessage, logLevel: finalLogLevel)
        
        // Step 12: Additional error context enhancement for specific scenarios
        // Handle cases where SDK returns generic errors but we can infer the actual issue
        var enhancedStatus = finalStatus
        var enhancedMessage = finalMessage
        
        // Special handling for unknown/onDifferentNetwork errors: try to infer the actual error
        // This addresses cases where the simplified SDK doesn't return specific error types
        if (finalStatus == StatusCase.unknown || finalStatus == StatusCase.networkAndOperatorMismatch) && !isTimeout {
            // Try to re-infer from the final message (in case we got more info)
            if let inferredStatus = Self.inferStatusFromMessage(finalMessage), inferredStatus != finalStatus {
                enhancedStatus = inferredStatus
                enhancedMessage = finalMessage
                if Self.debugEnabled {
                    Self.logDebug("Enhanced status from '\(finalStatus)' to '\(inferredStatus)' based on message: '\(finalMessage)'")
                }
            }
            // If still unknown, check network state for network-related errors
            else if finalStatus == StatusCase.unknown {
                if networkStateAfter.hasNoNetwork {
                    // No network at all
                    enhancedStatus = StatusCase.networkUnavailable
                    enhancedMessage = "Network unavailable"
                    if Self.debugEnabled {
                        Self.logDebug("Enhanced unknown error to networkUnavailable based on network state")
                    }
                } else if networkStateAfter.hasWifiOnly {
                    // WiFi only, no cellular data
                    enhancedStatus = StatusCase.wifiDetectedAndNoDataNetwork
                    enhancedMessage = "WiFi and no data network"
                    if Self.debugEnabled {
                        Self.logDebug("Enhanced unknown error to wifiDetectedAndNoDataNetwork based on network state")
                    }
                } else {
                    // Network is available but SDK returned unknown
                    // This could be:
                    // - Operator/network mismatch (e.g., Jio sim with Airtel data) -> networkAndOperatorMismatch
                    // - Operator not supported (e.g., BSNL/VI number with Airtel data) -> operatorNotSupported
                    // Since the simplified SDK doesn't distinguish these reliably, we try message inference
                    // If that fails, we keep it as unknown but log for debugging
                    if Self.debugEnabled {
                        Self.logDebug("Unknown error with available network - may indicate operator/network mismatch or operator not supported. Network: WiFi=\(networkStateAfter.isWifiAvailable), Cellular=\(networkStateAfter.isCellularAvailable), Message: '\(finalMessage)'")
                    }
                }
            }
        }
        
        // Calculate latency metrics
        let totalLatency = Int(elapsedTime * 1000) // Convert to milliseconds
        let initLatency = 0 // iOS SDK doesn't expose init time separately, using 0
        let authLatency = totalLatency // For iOS, auth latency is same as total
        
        // Return result with status enum case name, message, and latency metrics
        let result: [String: Any] = [
            ResultKey.status: enhancedStatus as NSString,
            ResultKey.message: enhancedMessage as NSString,
            "totalLatency": totalLatency,
            "initLatency": initLatency,
            "authLatency": authLatency
        ]
        Self.logDebug("authenticate: Returning authentication result - status: \(enhancedStatus), message: \(enhancedMessage), totalLatency: \(totalLatency)ms, initLatency: \(initLatency)ms, authLatency: \(authLatency)ms")
        Self.logInfo("authenticate: [LATENCY] Total: \(totalLatency)ms, Init: \(initLatency)ms, Auth: \(authLatency)ms")
        resolve(result)
            }
        }
    }
}
