WarplyConfiguration.swift 14.2 KB
//
//  WarplyConfiguration.swift
//  SwiftWarplyFramework
//
//  Created by Warply on 25/6/25.
//

import Foundation

/// Main configuration container for the SwiftWarplyFramework
/// Provides comprehensive control over all framework behavior including security, performance, and logging
public struct WarplyConfiguration {
    
    // MARK: - Component Configurations
    
    /// Database and encryption configuration
    public var databaseConfig: WarplyDatabaseConfig = WarplyDatabaseConfig()
    
    /// Token refresh and authentication configuration
    public var tokenConfig: WarplyTokenConfig = WarplyTokenConfig()
    
    /// Logging and debugging configuration
    public var loggingConfig: WarplyLoggingConfig = WarplyLoggingConfig()
    
    /// Network and connectivity configuration
    public var networkConfig: WarplyNetworkConfig = WarplyNetworkConfig()
    
    // MARK: - Global Framework Settings
    
    /// Enable analytics event collection and reporting
    public var enableAnalytics: Bool = true
    
    /// Enable crash reporting and error analytics
    public var enableCrashReporting: Bool = false
    
    /// Enable automatic device registration on SDK initialization
    public var enableAutoRegistration: Bool = true
    
    /// Framework version for compatibility tracking
    public let frameworkVersion: String = "2.5.1"
    
    // MARK: - Initialization
    
    /// Creates a new configuration with default settings
    /// All defaults are production-ready and secure
    public init() {}
    
    // MARK: - Validation
    
    /// Validates all configuration components
    /// - Throws: ConfigurationError if any configuration is invalid
    public func validate() throws {
        try databaseConfig.validate()
        try tokenConfig.validate()
        try loggingConfig.validate()
        try networkConfig.validate()
        
        print("✅ [WarplyConfiguration] All configuration components validated successfully")
    }
    
    // MARK: - Configuration Summary
    
    /// Returns a summary of the current configuration for debugging
    /// - Returns: Dictionary with configuration summary (no sensitive data)
    public func getSummary() -> [String: Any] {
        return [
            "frameworkVersion": frameworkVersion,
            "enableAnalytics": enableAnalytics,
            "enableCrashReporting": enableCrashReporting,
            "enableAutoRegistration": enableAutoRegistration,
            "database": databaseConfig.getSummary(),
            "token": tokenConfig.getSummary(),
            "logging": loggingConfig.getSummary(),
            "network": networkConfig.getSummary()
        ]
    }
}

// MARK: - Preset Configurations

extension WarplyConfiguration {
    
    /// Development configuration with verbose logging and debugging features
    /// - Encryption disabled for easier debugging
    /// - Verbose logging enabled
    /// - All debugging features enabled
    public static var development: WarplyConfiguration {
        var config = WarplyConfiguration()
        
        // Development-friendly database settings
        config.databaseConfig.encryptionEnabled = false
        config.databaseConfig.enableWALMode = true
        config.databaseConfig.cacheSize = 1000
        
        // Verbose logging for debugging
        config.loggingConfig.logLevel = .verbose
        config.loggingConfig.enableDatabaseLogging = true
        config.loggingConfig.enableNetworkLogging = true
        config.loggingConfig.enableTokenLogging = true
        config.loggingConfig.enablePerformanceLogging = true
        config.loggingConfig.maskSensitiveData = false // Show full data in development
        
        // Faster timeouts for development
        config.networkConfig.requestTimeout = 15.0
        config.networkConfig.maxRetryAttempts = 2
        
        // Reduced token retry for faster development cycles
        config.tokenConfig.maxRetryAttempts = 2
        config.tokenConfig.retryDelays = [0.0, 1.0]
        
        print("🔧 [WarplyConfiguration] Development configuration loaded")
        return config
    }
    
    /// Production configuration with security and performance optimizations
    /// - Encryption enabled by default
    /// - Minimal logging for performance
    /// - Conservative retry policies
    public static var production: WarplyConfiguration {
        var config = WarplyConfiguration()
        
        // Production security settings
        config.databaseConfig.encryptionEnabled = true
        config.databaseConfig.dataProtectionClass = .complete
        config.databaseConfig.enableWALMode = true
        config.databaseConfig.cacheSize = 2000
        
        // Minimal logging for performance
        config.loggingConfig.logLevel = .warning
        config.loggingConfig.enableDatabaseLogging = false
        config.loggingConfig.enableNetworkLogging = false
        config.loggingConfig.enableTokenLogging = false
        config.loggingConfig.enablePerformanceLogging = false
        config.loggingConfig.maskSensitiveData = true
        
        // Production network settings
        config.networkConfig.requestTimeout = 30.0
        config.networkConfig.maxRetryAttempts = 3
        config.networkConfig.enableExponentialBackoff = true
        
        // Standard token refresh settings
        config.tokenConfig.maxRetryAttempts = 3
        config.tokenConfig.retryDelays = [0.0, 1.0, 5.0]
        config.tokenConfig.circuitBreakerThreshold = 5
        
        // Enable crash reporting in production
        config.enableCrashReporting = true
        
        print("🏭 [WarplyConfiguration] Production configuration loaded")
        return config
    }
    
    /// Testing configuration optimized for unit and integration tests
    /// - Minimal logging to reduce test noise
    /// - Fast timeouts for quick test execution
    /// - Reduced retry attempts
    public static var testing: WarplyConfiguration {
        var config = WarplyConfiguration()
        
        // Testing database settings
        config.databaseConfig.encryptionEnabled = false
        config.databaseConfig.enableWALMode = false
        config.databaseConfig.cacheSize = 500
        
        // Minimal logging for clean test output
        config.loggingConfig.logLevel = .error
        config.loggingConfig.enableDatabaseLogging = false
        config.loggingConfig.enableNetworkLogging = false
        config.loggingConfig.enableTokenLogging = false
        config.loggingConfig.enablePerformanceLogging = false
        
        // Fast timeouts for quick tests
        config.networkConfig.requestTimeout = 5.0
        config.networkConfig.resourceTimeout = 10.0
        config.networkConfig.maxRetryAttempts = 1
        config.networkConfig.retryDelay = 0.1
        config.networkConfig.enableExponentialBackoff = false
        
        // Minimal token retry for fast tests
        config.tokenConfig.maxRetryAttempts = 1
        config.tokenConfig.retryDelays = [0.0]
        config.tokenConfig.refreshThresholdMinutes = 1
        config.tokenConfig.circuitBreakerThreshold = 2
        
        // Disable analytics in tests
        config.enableAnalytics = false
        config.enableCrashReporting = false
        config.enableAutoRegistration = false
        
        print("🧪 [WarplyConfiguration] Testing configuration loaded")
        return config
    }
    
    /// High-security configuration for sensitive environments
    /// - Maximum encryption and security features
    /// - Minimal logging to prevent data leakage
    /// - Conservative network policies
    public static var highSecurity: WarplyConfiguration {
        var config = WarplyConfiguration()
        
        // Maximum security database settings
        config.databaseConfig.encryptionEnabled = true
        config.databaseConfig.dataProtectionClass = .completeUntilFirstUserAuthentication
        config.databaseConfig.useKeychainForKeys = true
        config.databaseConfig.enableWALMode = true
        
        // Minimal logging for security
        config.loggingConfig.logLevel = .error
        config.loggingConfig.enableDatabaseLogging = false
        config.loggingConfig.enableNetworkLogging = false
        config.loggingConfig.enableTokenLogging = false
        config.loggingConfig.enablePerformanceLogging = false
        config.loggingConfig.maskSensitiveData = true
        config.loggingConfig.enableFileLogging = false
        
        // Conservative network settings
        config.networkConfig.requestTimeout = 20.0
        config.networkConfig.maxRetryAttempts = 2
        config.networkConfig.allowsCellularAccess = false // WiFi only
        
        // Conservative token settings
        config.tokenConfig.refreshThresholdMinutes = 10 // Refresh earlier
        config.tokenConfig.maxRetryAttempts = 2
        config.tokenConfig.retryDelays = [0.0, 2.0]
        config.tokenConfig.circuitBreakerThreshold = 3
        
        // Disable optional features for security
        config.enableCrashReporting = false
        
        print("🔒 [WarplyConfiguration] High-security configuration loaded")
        return config
    }
}

// MARK: - Codable Support

extension WarplyConfiguration: Codable {
    
    // Custom coding keys to exclude frameworkVersion from Codable
    private enum CodingKeys: String, CodingKey {
        case databaseConfig
        case tokenConfig
        case loggingConfig
        case networkConfig
        case enableAnalytics
        case enableCrashReporting
        case enableAutoRegistration
        // frameworkVersion is excluded - it's a constant that shouldn't be encoded/decoded
    }
    
    /// Saves configuration to JSON data
    /// - Returns: JSON data representation of the configuration
    /// - Throws: EncodingError if serialization fails
    public func toJSONData() throws -> Data {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        return try encoder.encode(self)
    }
    
    /// Creates configuration from JSON data
    /// - Parameter data: JSON data containing configuration
    /// - Returns: WarplyConfiguration instance
    /// - Throws: DecodingError if deserialization fails
    public static func fromJSONData(_ data: Data) throws -> WarplyConfiguration {
        let decoder = JSONDecoder()
        return try decoder.decode(WarplyConfiguration.self, from: data)
    }
    
    /// Saves configuration to file
    /// - Parameter url: File URL to save configuration
    /// - Throws: Error if file writing fails
    public func saveToFile(at url: URL) throws {
        let data = try toJSONData()
        try data.write(to: url)
        print("💾 [WarplyConfiguration] Configuration saved to: \(url.path)")
    }
    
    /// Loads configuration from file
    /// - Parameter url: File URL to load configuration from
    /// - Returns: WarplyConfiguration instance
    /// - Throws: Error if file reading or parsing fails
    public static func loadFromFile(at url: URL) throws -> WarplyConfiguration {
        let data = try Data(contentsOf: url)
        let config = try fromJSONData(data)
        print("📂 [WarplyConfiguration] Configuration loaded from: \(url.path)")
        return config
    }
}

// MARK: - Configuration Errors

/// Errors that can occur during configuration validation or processing
public enum ConfigurationError: Error, LocalizedError {
    case invalidRefreshThreshold(Int)
    case invalidRetryAttempts(Int)
    case retryDelaysMismatch(expected: Int, actual: Int)
    case invalidLogLevel(String)
    case invalidTimeout(TimeInterval)
    case invalidKeyIdentifier(String)
    case invalidCacheSize(Int)
    case invalidCircuitBreakerThreshold(Int)
    case invalidFileSize(Int)
    case configurationValidationFailed([String])
    
    public var errorDescription: String? {
        switch self {
        case .invalidRefreshThreshold(let minutes):
            return "Invalid refresh threshold: \(minutes) minutes. Must be between 1 and 60 minutes."
        case .invalidRetryAttempts(let attempts):
            return "Invalid retry attempts: \(attempts). Must be between 1 and 10 attempts."
        case .retryDelaysMismatch(let expected, let actual):
            return "Retry delays count mismatch: expected \(expected) delays, got \(actual)."
        case .invalidLogLevel(let level):
            return "Invalid log level: \(level). Must be a valid WarplyLogLevel."
        case .invalidTimeout(let timeout):
            return "Invalid timeout: \(timeout) seconds. Must be between 1 and 300 seconds."
        case .invalidKeyIdentifier(let identifier):
            return "Invalid key identifier: \(identifier). Must be a non-empty string."
        case .invalidCacheSize(let size):
            return "Invalid cache size: \(size). Must be between 100 and 10000."
        case .invalidCircuitBreakerThreshold(let threshold):
            return "Invalid circuit breaker threshold: \(threshold). Must be between 1 and 20."
        case .invalidFileSize(let size):
            return "Invalid file size: \(size) bytes. Must be between 1MB and 100MB."
        case .configurationValidationFailed(let errors):
            return "Configuration validation failed with errors: \(errors.joined(separator: ", "))"
        }
    }
    
    public var recoverySuggestion: String? {
        switch self {
        case .invalidRefreshThreshold:
            return "Use a refresh threshold between 1 and 60 minutes. Recommended: 5 minutes."
        case .invalidRetryAttempts:
            return "Use between 1 and 10 retry attempts. Recommended: 3 attempts."
        case .retryDelaysMismatch:
            return "Ensure the retry delays array has the same count as maxRetryAttempts."
        case .invalidLogLevel:
            return "Use one of: .none, .error, .warning, .info, .debug, .verbose"
        case .invalidTimeout:
            return "Use a timeout between 1 and 300 seconds. Recommended: 30 seconds."
        case .invalidKeyIdentifier:
            return "Provide a non-empty string for the key identifier."
        case .invalidCacheSize:
            return "Use a cache size between 100 and 10000. Recommended: 2000."
        case .invalidCircuitBreakerThreshold:
            return "Use a threshold between 1 and 20. Recommended: 5."
        case .invalidFileSize:
            return "Use a file size between 1MB and 100MB. Recommended: 10MB."
        case .configurationValidationFailed:
            return "Fix all validation errors and try again."
        }
    }
}