import { NativeModules, Platform } from 'react-native';

/**
 * Environment constants
 */
const ENVIRONMENT = {
  PRODUCTION: 'Production',
  SANDBOX: 'Sandbox',
} as const;

/**
 * Debug logging flag - disabled by default
 */
let debugEnabled = false;

/**
 * Log levels for debug logging
 */
enum LogLevel {
  DEBUG = 'DEBUG',
  INFO = 'INFO',
  ERROR = 'ERROR',
}

/**
 * Log entry structure for storage
 */
export interface LogEntry {
  id: string;
  timestamp: number;
  level: 'DEBUG' | 'INFO' | 'ERROR';
  message: string;
  data?: any;
}

/**
 * Maximum number of logs to store (FIFO when limit reached)
 */
const MAX_LOG_ENTRIES = 1000;

/**
 * In-memory log storage array
 */
const logStorage: LogEntry[] = [];

/**
 * Generates a unique ID for log entries
 */
function generateLogId(): string {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}

/**
 * Adds a log entry to storage, removing oldest if limit reached
 */
function addLogToStorage(level: LogLevel, message: string, data?: any): void {
  const logEntry: LogEntry = Object.freeze({
    id: generateLogId(),
    timestamp: Date.now(),
    level,
    message,
    data,
  });

  logStorage.push(logEntry);

  // Remove oldest logs if limit exceeded (FIFO)
  if (logStorage.length > MAX_LOG_ENTRIES) {
    logStorage.shift();
  }
}

/**
 * Internal logger utility that checks debug flag before logging
 */
const logger = {
  debug: (message: string, ...args: any[]) => {
    if (debugEnabled) {
      console.debug(`[BureauSDK] [${LogLevel.DEBUG}] ${message}`, ...args);
      addLogToStorage(
        LogLevel.DEBUG,
        message,
        args.length > 0 ? args : undefined
      );
    }
  },
  info: (message: string, ...args: any[]) => {
    if (debugEnabled) {
      console.log(`[BureauSDK] [${LogLevel.INFO}] ${message}`, ...args);
      addLogToStorage(
        LogLevel.INFO,
        message,
        args.length > 0 ? args : undefined
      );
    }
  },
  error: (message: string, ...args: any[]) => {
    if (debugEnabled) {
      console.error(`[BureauSDK] [${LogLevel.ERROR}] ${message}`, ...args);
      addLogToStorage(
        LogLevel.ERROR,
        message,
        args.length > 0 ? args : undefined
      );
    }
  },
};

/**
 * Masks sensitive data for logging (shows first 4 and last 4 characters)
 */
function maskSensitiveData(value: string): string {
  if (!value || value.length <= 8) {
    return '***';
  }
  return `${value.substring(0, 4)}...${value.substring(value.length - 4)}`;
}

/**
 * Default timeout in milliseconds (15 seconds)
 */
const DEFAULT_TIMEOUT_MS = 15000;

/**
 * Result keys for authentication response
 */
const RESULT_KEYS = {
  STATUS: 'status',
  MESSAGE: 'message',
} as const;

/**
 * Error messages
 */
const ERROR_MESSAGES = {
  INVALID_AUTHENTICATION_RESULT:
    'Invalid authentication result from native module',
} as const;

/**
 * Validates that the authentication result from native module has the expected structure
 *
 * @param result - The result object from native module
 * @returns True if result is valid, false otherwise
 */
function validateAuthenticationResult(result: any): boolean {
  return (
    result &&
    typeof result[RESULT_KEYS.STATUS] === 'string' &&
    typeof result[RESULT_KEYS.MESSAGE] === 'string'
  );
}

/**
 * Detects if a timeout occurred based on result status and elapsed time
 *
 * @param result - The authentication result object
 * @param timeOutInMs - Configured timeout duration in milliseconds
 * @param startTime - Timestamp when authentication started
 * @returns True if timeout is detected, false otherwise
 */
function detectTimeoutInResult(
  result: any,
  timeOutInMs: number,
  startTime: number
): boolean {
  if (!result || !result[RESULT_KEYS.STATUS]) {
    return false;
  }

  const elapsedTime = Date.now() - startTime;
  const status = result[RESULT_KEYS.STATUS] as string;

  // Timeout detected if:
  // 1. Status is 'awaitingProviderAck' and elapsed time >= timeout duration
  // 2. Status is 'unknown' and elapsed time >= timeout duration
  // 3. Status is explicitly 'timeout' and elapsed time >= timeout duration
  const isTimeoutCandidate =
    status === 'awaitingProviderAck' ||
    status === 'unknown' ||
    status === 'timeout';

  return isTimeoutCandidate && elapsedTime >= timeOutInMs;
}

/**
 * Creates a standardized timeout error log context object
 *
 * @param clientId - Bureau client ID (will be masked)
 * @param sessionId - Session ID (will be masked)
 * @param timeOutInMs - Configured timeout duration in milliseconds
 * @param elapsedTime - Actual elapsed time in milliseconds
 * @param env - Environment (Production/Sandbox)
 * @returns Object with timeout logging context
 */
function createTimeoutErrorLogContext(
  clientId: string,
  sessionId: string,
  timeOutInMs: number,
  elapsedTime: number,
  env: string
): Record<string, unknown> {
  const percentage =
    timeOutInMs > 0 ? ((elapsedTime / timeOutInMs) * 100).toFixed(1) : 'N/A';
  return {
    event: 'Timeout error detected',
    duration: `${timeOutInMs}ms`,
    elapsed: `${elapsedTime}ms (${percentage}% of timeout)`,
    sessionId: maskSensitiveData(sessionId),
    clientId: maskSensitiveData(clientId),
    env,
    platform: Platform.OS,
  };
}

/**
 * Error message shown when the native module is not properly linked
 */
const LINKING_ERROR =
  `The package 'react-native-bureauid-fraud-sdk' doesn't seem to be linked. Make sure: \n\n` +
  Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
  '- You rebuilt the app after installing the package\n' +
  '- You are not using Expo Go\n';

/**
 * Native module bridge to BureauOtlModule
 * Provides a proxy that throws a helpful error if the module is not linked
 */
const BureauOtlModule = NativeModules.BureauOtlModule
  ? NativeModules.BureauOtlModule
  : new Proxy(
      {},
      {
        get() {
          throw new Error(LINKING_ERROR);
        },
      }
    );

/**
 * AuthenticationStatus enum values matching the native SDK
 * This represents all possible authentication status cases from Bureau SDK
 */
export type AuthenticationStatus =
  | 'networkAndOperatorMismatch'
  | 'authFailure'
  | 'networkNotSupported'
  | 'operatorNotSupported'
  | 'operatorAndNetworkNotSupported'
  | 'awaitingProviderAck'
  | 'authValidationError'
  | 'duplicateCorrelationId'
  | 'integrationFailure'
  | 'authStateExpired'
  | 'unauthorized'
  | 'countryNotSupported'
  | 'rateLimitExceeded'
  | 'internalServerError'
  | 'networkUnavailable'
  | 'wifiDetectedAndNoDataNetwork'
  | 'completed'
  | 'timeout'
  | 'unknown';

/**
 * Authentication result containing the full AuthenticationStatus enum and all properties
 * This is a structured object returned from the native SDK, matching the SDK's AuthenticationStatus
 */
export interface AuthenticationResult {
  /**
   * Authentication status enum case name from SDK
   * This matches the AuthenticationStatus enum from iOS/Android SDKs
   */
  readonly status: AuthenticationStatus;
  /**
   * Human-readable message from SDK (associated value from enum)
   */
  readonly message: string;
  /**
   * Total latency in milliseconds (from start to finish)
   */
  totalLatency?: number;
  /**
   * SDK initialization latency in milliseconds
   */
  initLatency?: number;
  /**
   * Authentication call latency in milliseconds
   */
  authLatency?: number;
}

/**
 * Creates a properly typed AuthenticationResult object
 * This ensures type safety and proper object structure
 */
function createAuthenticationResult(
  status: AuthenticationStatus,
  message: string,
  latency?: {
    totalLatency?: number;
    initLatency?: number;
    authLatency?: number;
  }
): AuthenticationResult {
  const result: AuthenticationResult = {
    status,
    message,
  };

  // Add latency fields if provided
  if (latency) {
    if (latency.totalLatency !== undefined) {
      result.totalLatency = latency.totalLatency;
    }
    if (latency.initLatency !== undefined) {
      result.initLatency = latency.initLatency;
    }
    if (latency.authLatency !== undefined) {
      result.authLatency = latency.authLatency;
    }
  }

  return Object.freeze(result);
}

/**
 * Enables or disables debug logging
 *
 * @param enabled - Whether to enable debug logging
 */
export function setDebugEnabled(enabled: boolean): void {
  // Store the previous state for logging purposes
  const previousState = debugEnabled;

  // If we are turning logging off, log this event before disabling it
  if (!enabled && previousState) {
    // Log the disable event while logging is still enabled
    console.log(`[BureauSDK] [INFO] Debug logging disabled`);
    addLogToStorage(LogLevel.INFO, 'Debug logging disabled');
  }

  // Update the debug enabled state
  debugEnabled = enabled;

  // If we are turning logging on, log this event after enabling it
  if (enabled) {
    console.log(`[BureauSDK] [INFO] Debug logging enabled`);
    addLogToStorage(LogLevel.INFO, 'Debug logging enabled');
  }

  // Also set debug flag on native modules (use proxy so missing linkage throws a clear error)
  const nativeModule = BureauOtlModule as any;
  if (nativeModule && typeof nativeModule.setDebugEnabled === 'function') {
    try {
      nativeModule.setDebugEnabled(enabled);
    } catch (error) {
      // Log warning if we fail to set native debug state, but only if logging is enabled
      if (enabled) {
        console.warn(
          '[BureauSDK] Failed to enable debug logging on native module:',
          error
        );
      }
    }
  }
}

/**
 * Fetches native SDK logs from the native modules (iOS/Android)
 * Merges them with React Native logs and returns combined list
 * @returns Promise resolving to array of log entries (most recent first)
 */
export async function fetchNativeLogs(): Promise<LogEntry[]> {
  try {
    const nativeModule = BureauOtlModule as any;
    if (!nativeModule || typeof nativeModule.getNativeLogs !== 'function') {
      return [];
    }

    const nativeLogs = await nativeModule.getNativeLogs();
    if (!Array.isArray(nativeLogs)) {
      return [];
    }

    // Convert native log format to LogEntry format
    const convertedLogs: LogEntry[] = nativeLogs.map((log: any) => ({
      id: log.id || generateLogId(),
      timestamp: log.timestamp || Date.now(),
      level: (log.level || 'INFO').toUpperCase() as 'DEBUG' | 'INFO' | 'ERROR',
      message: log.message || '',
      data: log.data,
    }));

    return convertedLogs;
  } catch (error) {
    if (debugEnabled) {
      logger.error('Failed to fetch native logs', { error });
    }
    return [];
  }
}

/**
 * Retrieves all stored log entries, including native logs
 * Fetches native logs and merges with React Native logs
 * @returns Promise resolving to array of log entries (most recent first)
 */
export async function getLogs(): Promise<LogEntry[]> {
  const reactNativeLogs = [...logStorage];

  // Fetch native logs if debug is enabled
  let nativeLogs: LogEntry[] = [];
  if (debugEnabled) {
    try {
      nativeLogs = await fetchNativeLogs();
    } catch (error) {
      // Silently fail if native logs can't be fetched
      if (debugEnabled) {
        logger.debug('Could not fetch native logs', { error });
      }
    }
  }

  // Merge logs and sort by timestamp (most recent first)
  const allLogs = [...reactNativeLogs, ...nativeLogs];
  allLogs.sort((a, b) => b.timestamp - a.timestamp);

  return allLogs;
}

/**
 * Retrieves all stored log entries synchronously (React Native logs only)
 * For backward compatibility - use getLogs() for native logs
 * @returns Array of log entries (most recent first)
 */
export function getLogsSync(): LogEntry[] {
  return [...logStorage].reverse(); // Return reversed to show most recent first
}

/**
 * Retrieves log entries filtered by level, including native logs
 * @param level - Log level to filter by
 * @returns Promise resolving to array of log entries matching the level (most recent first)
 */
export async function getLogsByLevel(
  level: 'DEBUG' | 'INFO' | 'ERROR'
): Promise<LogEntry[]> {
  const allLogs = await getLogs();
  return allLogs.filter((log) => log.level === level);
}

/**
 * Retrieves log entries filtered by level synchronously (React Native logs only)
 * For backward compatibility - use getLogsByLevel() for native logs
 * @param level - Log level to filter by
 * @returns Array of log entries matching the level (most recent first)
 */
export function getLogsByLevelSync(
  level: 'DEBUG' | 'INFO' | 'ERROR'
): LogEntry[] {
  return logStorage.filter((log) => log.level === level).reverse(); // Return reversed to show most recent first
}

/**
 * Clears all stored log entries
 */
export function clearLogs(): void {
  logStorage.length = 0;
}

/**
 * Gets the current debug enabled state
 * @returns True if debug logging is enabled, false otherwise
 */
export function isDebugEnabled(): boolean {
  return debugEnabled;
}

/**
 * Validates MSISDN format against allowed country codes
 *
 * @param msisdn - Mobile number as string
 * @param allowedCodes - Array of allowed country code prefixes (e.g., ["91", "1"])
 * @returns True if MSISDN starts with an allowed country code, false otherwise
 */
function validateMSISDNFormat(msisdn: string, allowedCodes: string[]): boolean {
  if (debugEnabled) {
    logger.debug('[validateMSISDNFormat] Entry', {
      msisdn: maskSensitiveData(msisdn),
      allowedCodesCount: allowedCodes.length,
      allowedCodes: allowedCodes,
    });
  }

  if (allowedCodes.length === 0) {
    // If no codes specified, allow all (backward compatibility)
    if (debugEnabled) {
      logger.debug(
        '[validateMSISDNFormat] No codes specified - allowing all (backward compatibility)'
      );
    }
    return true;
  }

  // Remove any non-digit characters for validation
  const digitsOnly = msisdn.replace(/\D/g, '');
  if (debugEnabled) {
    logger.debug('[validateMSISDNFormat] Step 1: Extracted digits', {
      originalLength: msisdn.length,
      digitsOnlyLength: digitsOnly.length,
      digitsOnly: maskSensitiveData(digitsOnly),
    });
  }

  const isValid = allowedCodes.some((code) => {
    const matches = digitsOnly.startsWith(code);
    if (debugEnabled) {
      logger.debug('[validateMSISDNFormat] Step 2: Checking code', {
        code,
        matches,
        digitsStart: maskSensitiveData(digitsOnly.substring(0, code.length)),
      });
    }
    return matches;
  });

  if (debugEnabled) {
    logger.debug('[validateMSISDNFormat] Result', {
      isValid,
      checkedCodes: allowedCodes,
    });
  }

  return isValid;
}

/**
 * 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' (default: 'Production')
 * @param timeOutInMs - Timeout in milliseconds (default: 15000)
 * @param allowedCountryCodes - Optional array of allowed country codes (e.g., ["91", "1"]). Defaults to ["91"] if not provided
 * @param pspCallbacks - Optional PSP callback keys for the Initiate request (iOS BureauSMV 1.2.1+)
 * @returns Promise resolving to AuthenticationResult with status enum and message from SDK
 */
export function authenticate(
  clientId: String,
  sessionId: String,
  msisdn: String,
  env: String = ENVIRONMENT.PRODUCTION,
  timeOutInMs: number = DEFAULT_TIMEOUT_MS,
  allowedCountryCodes?: string[],
  pspCallbacks?: string[]
): Promise<AuthenticationResult> {
  const startTime = Date.now();
  const functionName = '[authenticate]';
  const pspCallbackKeys = (pspCallbacks ?? [])
    .map((key) => String(key).trim())
    .filter((key) => key.length > 0);

  // Step 1: Log function entry with all parameters
  if (debugEnabled) {
    logger.debug(`${functionName} Entry - Starting authentication`, {
      clientId: maskSensitiveData(String(clientId)),
      sessionId: maskSensitiveData(String(sessionId)),
      msisdn: maskSensitiveData(String(msisdn)),
      env: String(env),
      timeOutInMs,
      allowedCountryCodesProvided: allowedCountryCodes !== undefined,
      allowedCountryCodesCount: allowedCountryCodes?.length || 0,
      allowedCountryCodes: allowedCountryCodes || [],
      pspCallbacksCount: pspCallbackKeys.length,
      platform: Platform.OS,
      timestamp: startTime,
    });
  }

  // Step 2: Parameter normalization and transformation
  const paramNormalizationStart = Date.now();
  const clientIdStr = String(clientId);
  const sessionIdStr = String(sessionId);
  const msisdnStr = String(msisdn);
  const envStr = String(env);
  const paramNormalizationTime = Date.now() - paramNormalizationStart;

  if (debugEnabled) {
    logger.debug(`${functionName} Step 1: Parameter normalization`, {
      clientIdType: typeof clientId,
      sessionIdType: typeof sessionId,
      msisdnType: typeof msisdn,
      envType: typeof env,
      timeOutInMsType: typeof timeOutInMs,
      normalizationTimeMs: paramNormalizationTime,
      normalizedValues: {
        clientId: maskSensitiveData(clientIdStr),
        sessionId: maskSensitiveData(sessionIdStr),
        msisdn: maskSensitiveData(msisdnStr),
        env: envStr,
        timeOutInMs,
      },
    });
  }

  // Step 3: Country code default handling
  const countryCodeHandlingStart = Date.now();
  const countryCodes =
    allowedCountryCodes && allowedCountryCodes.length > 0
      ? allowedCountryCodes
      : ['91']; // Default to ["91"] for backward compatibility
  const countryCodeHandlingTime = Date.now() - countryCodeHandlingStart;

  if (debugEnabled) {
    logger.debug(`${functionName} Step 2: Country code handling`, {
      provided: allowedCountryCodes !== undefined,
      providedCount: allowedCountryCodes?.length || 0,
      usingDefault: !allowedCountryCodes || allowedCountryCodes.length === 0,
      finalCountryCodes: countryCodes,
      handlingTimeMs: countryCodeHandlingTime,
    });
  }

  // Step 4: Country code validation
  const validationStart = Date.now();
  const validationResult = validateMSISDNFormat(msisdnStr, countryCodes);
  const validationTime = Date.now() - validationStart;

  if (debugEnabled) {
    logger.debug(`${functionName} Step 3: Country code validation`, {
      msisdn: maskSensitiveData(msisdnStr),
      allowedCodes: countryCodes,
      validationResult,
      validationTimeMs: validationTime,
      elapsedFromStart: Date.now() - startTime,
    });
  }

  if (!validationResult) {
    const codesStr = countryCodes.join(', ');
    const errorMessage = `Invalid country code. Supported codes: ${codesStr}`;
    const totalTime = Date.now() - startTime;

    if (debugEnabled) {
      logger.error(`${functionName} Step 3: Validation failed`, {
        msisdn: maskSensitiveData(msisdnStr),
        allowedCodes: countryCodes,
        errorMessage,
        totalExecutionTimeMs: totalTime,
      });
    }

    const authResult = createAuthenticationResult(
      'authValidationError',
      errorMessage
    );

    if (debugEnabled) {
      logger.debug(
        `${functionName} Step 3: Returning validation error result`,
        {
          result: authResult,
          totalExecutionTimeMs: totalTime,
        }
      );
    }

    return Promise.resolve(authResult);
  }

  if (debugEnabled) {
    logger.debug(
      `${functionName} Step 3: Validation passed - proceeding to native call`
    );
  }

  // Step 5: Prepare native module call
  const nativeCallPrepStart = Date.now();
  const nativeCallParams = {
    clientId: clientIdStr,
    sessionId: sessionIdStr,
    msisdn: msisdnStr,
    env: envStr.toLowerCase(),
    timeout: timeOutInMs,
    allowedCountryCodes: countryCodes,
    pspCallbacks: pspCallbackKeys,
  };
  const nativeCallPrepTime = Date.now() - nativeCallPrepStart;

  if (debugEnabled) {
    logger.debug(`${functionName} Step 4: Native module call preparation`, {
      params: {
        clientId: maskSensitiveData(nativeCallParams.clientId),
        sessionId: maskSensitiveData(nativeCallParams.sessionId),
        msisdn: maskSensitiveData(nativeCallParams.msisdn),
        env: nativeCallParams.env,
        timeout: nativeCallParams.timeout,
        allowedCountryCodes: nativeCallParams.allowedCountryCodes,
        pspCallbacksCount: nativeCallParams.pspCallbacks.length,
      },
      prepTimeMs: nativeCallPrepTime,
      elapsedFromStart: Date.now() - startTime,
    });
  }

  // Step 6: Make native module call
  const nativeCallStart = Date.now();
  if (debugEnabled) {
    logger.debug(`${functionName} Step 5: Calling native module`, {
      moduleAvailable: !!BureauOtlModule,
      callStartTime: nativeCallStart,
    });
  }

  return BureauOtlModule.authenticate(
    clientIdStr,
    sessionIdStr,
    msisdnStr,
    envStr.toLowerCase(),
    timeOutInMs,
    countryCodes,
    pspCallbackKeys
  )
    .then((result: any) => {
      const nativeCallTime = Date.now() - nativeCallStart;
      const responseReceivedTime = Date.now();

      // Step 7: Log native module response received
      if (debugEnabled) {
        logger.debug(
          `${functionName} Step 6: Native module response received`,
          {
            hasResult: !!result,
            resultType: typeof result,
            resultIsNull: result === null,
            resultIsUndefined: result === undefined,
            resultKeys: result ? Object.keys(result) : [],
            nativeCallDurationMs: nativeCallTime,
            elapsedFromStart: responseReceivedTime - startTime,
            rawResult: result,
          }
        );
      }

      // Step 8: Validate result structure
      const resultValidationStart = Date.now();
      const isValidResult = validateAuthenticationResult(result);
      const resultValidationTime = Date.now() - resultValidationStart;

      if (debugEnabled) {
        logger.debug(`${functionName} Step 7: Result structure validation`, {
          isValid: isValidResult,
          hasResult: !!result,
          hasStatus: result && result[RESULT_KEYS.STATUS] !== undefined,
          hasMessage: result && result[RESULT_KEYS.MESSAGE] !== undefined,
          statusType: result && typeof result[RESULT_KEYS.STATUS],
          messageType: result && typeof result[RESULT_KEYS.MESSAGE],
          validationTimeMs: resultValidationTime,
          elapsedFromStart: Date.now() - startTime,
        });
      }

      if (!isValidResult) {
        const totalTime = Date.now() - startTime;
        logger.error(
          `${functionName} Step 7: Validation failed - Invalid result format`,
          {
            result,
            expectedStructure: {
              status: 'string',
              message: 'string',
            },
            actualStructure: result
              ? {
                  status: typeof result[RESULT_KEYS.STATUS],
                  message: typeof result[RESULT_KEYS.MESSAGE],
                }
              : 'null/undefined',
            totalExecutionTimeMs: totalTime,
          }
        );
        throw new Error(ERROR_MESSAGES.INVALID_AUTHENTICATION_RESULT);
      }

      // Step 9: Extract status, message, and latency
      const extractionStart = Date.now();
      const rawStatus = result[RESULT_KEYS.STATUS] as string;
      const rawMessage = result[RESULT_KEYS.MESSAGE] as string;
      // Extract latency fields from native result
      const latency = {
        totalLatency: result.totalLatency !== undefined ? Number(result.totalLatency) : undefined,
        initLatency: result.initLatency !== undefined ? Number(result.initLatency) : undefined,
        authLatency: result.authLatency !== undefined ? Number(result.authLatency) : undefined,
      };
      const extractionTime = Date.now() - extractionStart;

      if (debugEnabled) {
        logger.debug(`${functionName} Step 8: Status, message, and latency extraction`, {
          rawStatus,
          rawMessage,
          statusLength: rawStatus?.length,
          messageLength: rawMessage?.length,
          latency,
          extractionTimeMs: extractionTime,
          elapsedFromStart: Date.now() - startTime,
        });
      }

      // Step 10: Detect timeout scenarios
      const timeoutDetectionStart = Date.now();
      const elapsedTime = Date.now() - startTime;
      const isTimeout = detectTimeoutInResult(result, timeOutInMs, startTime);
      const timeoutDetectionTime = Date.now() - timeoutDetectionStart;

      if (debugEnabled) {
        logger.debug(`${functionName} Step 9: Timeout detection`, {
          elapsedTimeMs: elapsedTime,
          timeoutThresholdMs: timeOutInMs,
          status: rawStatus,
          isTimeoutCandidate:
            rawStatus === 'awaitingProviderAck' ||
            rawStatus === 'unknown' ||
            rawStatus === 'timeout',
          isTimeout,
          timeoutPercentage:
            timeOutInMs > 0
              ? ((elapsedTime / timeOutInMs) * 100).toFixed(1) + '%'
              : 'N/A',
          detectionTimeMs: timeoutDetectionTime,
          elapsedFromStart: Date.now() - startTime,
        });
      }

      // Step 11: Determine final status and message
      let finalStatus = rawStatus as AuthenticationStatus;
      let finalMessage = rawMessage;

      if (isTimeout) {
        finalStatus = 'timeout';
        finalMessage = `Authentication timeout after ${timeOutInMs}ms`;
        const timeoutContext = createTimeoutErrorLogContext(
          clientIdStr,
          sessionIdStr,
          timeOutInMs,
          elapsedTime,
          envStr
        );

        if (debugEnabled) {
          logger.error(
            `${functionName} Step 10: Timeout detected - updating status`,
            {
              originalStatus: rawStatus,
              originalMessage: rawMessage,
              finalStatus,
              finalMessage,
              timeoutContext,
              elapsedFromStart: Date.now() - startTime,
            }
          );
        }
      } else {
        if (debugEnabled) {
          logger.debug(
            `${functionName} Step 10: No timeout - using original status`,
            {
              finalStatus,
              finalMessage,
              elapsedFromStart: Date.now() - startTime,
            }
          );
        }
      }

      // Step 12: Create authentication result with latency
      const resultCreationStart = Date.now();
      const authResult = createAuthenticationResult(finalStatus, finalMessage, latency);
      const resultCreationTime = Date.now() - resultCreationStart;

      if (debugEnabled) {
        logger.debug(
          `${functionName} Step 11: Creating authentication result`,
          {
            result: authResult,
            creationTimeMs: resultCreationTime,
            elapsedFromStart: Date.now() - startTime,
          }
        );
      }

      // Step 13: Log final outcome
      const totalTime = Date.now() - startTime;
      if (isTimeout) {
        logger.error(`${functionName} Step 12: Authentication timed out`, {
          status: authResult.status,
          message: authResult.message,
          totalExecutionTimeMs: totalTime,
          nativeCallDurationMs: nativeCallTime,
          breakdown: {
            paramNormalization: paramNormalizationTime,
            countryCodeHandling: countryCodeHandlingTime,
            validation: validationTime,
            nativeCallPrep: nativeCallPrepTime,
            nativeCall: nativeCallTime,
            resultValidation: validationTime,
            statusExtraction: extractionTime,
            timeoutDetection: timeoutDetectionTime,
            resultCreation: resultCreationTime,
          },
        });
      } else {
        logger.info(`${functionName} Step 12: Authentication completed`, {
          status: authResult.status,
          message: authResult.message,
          totalExecutionTimeMs: totalTime,
          nativeCallDurationMs: nativeCallTime,
          breakdown: {
            paramNormalization: paramNormalizationTime,
            countryCodeHandling: countryCodeHandlingTime,
            validation: validationTime,
            nativeCallPrep: nativeCallPrepTime,
            nativeCall: nativeCallTime,
            resultValidation: validationTime,
            statusExtraction: extractionTime,
            timeoutDetection: timeoutDetectionTime,
            resultCreation: resultCreationTime,
          },
        });
      }

      return authResult;
    })
    .catch((error: any) => {
      const errorTime = Date.now();
      const totalTime = errorTime - startTime;
      const nativeCallTime = errorTime - nativeCallStart;

      if (debugEnabled) {
        logger.error(
          `${functionName} Step 13: Authentication failed with error`,
          {
            errorType: error?.constructor?.name || typeof error,
            errorMessage: error?.message || String(error),
            errorStack: error?.stack,
            errorName: error?.name,
            errorCode: error?.code,
            errorDetails: error,
            totalExecutionTimeMs: totalTime,
            nativeCallDurationMs: nativeCallTime,
            context: {
              clientId: maskSensitiveData(clientIdStr),
              sessionId: maskSensitiveData(sessionIdStr),
              msisdn: maskSensitiveData(msisdnStr),
              env: envStr,
              timeout: timeOutInMs,
              countryCodes,
              platform: Platform.OS,
            },
            timingBreakdown: {
              paramNormalization: paramNormalizationTime,
              countryCodeHandling: countryCodeHandlingTime,
              validation: validationTime,
              nativeCallPrep: nativeCallPrepTime,
              nativeCall: nativeCallTime,
            },
          }
        );
      }
      return Promise.reject(error);
    });
}
