import Foundation
import UIKit
#if os(iOS)
import SystemConfiguration
#endif

/// Device state collector
public struct DeviceStateCollector {
    
    /// Collect current device state
    public static func collect() -> DiagnosticsDeviceState {
        #if os(iOS)
        return DiagnosticsDeviceState(
            memoryMB: getMemoryUsage(),
            batteryLevel: getBatteryLevel(),
            isCharging: getIsCharging(),
            storageMB: getStorageUsage(),
            networkType: getNetworkType(),
            isOnline: getIsOnline(),
            thermalState: getThermalState()
        )
        #elseif os(macOS)
        return DiagnosticsDeviceState(
            memoryMB: getMemoryUsage(),
            batteryLevel: nil,
            isCharging: nil,
            storageMB: nil,
            networkType: nil,
            isOnline: getIsOnline(),
            thermalState: nil
        )
        #else
        return DiagnosticsDeviceState(
            memoryMB: nil,
            batteryLevel: nil,
            isCharging: nil,
            storageMB: nil,
            networkType: nil,
            isOnline: true,
            thermalState: nil
        )
        #endif
    }
    
    #if os(iOS)
    private static func getMemoryUsage() -> Int? {
        var info = mach_task_basic_info()
        var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
        
        let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
            $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
                task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
            }
        }
        
        guard kerr == KERN_SUCCESS else { return nil }
        return Int(info.resident_size / 1024 / 1024)
    }
    
    private static func getBatteryLevel() -> Double? {
        UIDevice.current.isBatteryMonitoringEnabled = true
        return Double(UIDevice.current.batteryLevel)
    }
    
    private static func getIsCharging() -> Bool? {
        UIDevice.current.isBatteryMonitoringEnabled = true
        return UIDevice.current.batteryState == .charging
    }
    
    private static func getStorageUsage() -> Int? {
        do {
            let attributes = try FileManager.default.attributesOfFileSystem(forPath: NSHomeDirectory())
            if let totalSize = attributes[.systemSize] as? NSNumber,
               let freeSize = attributes[.systemFreeSize] as? NSNumber {
                let usedSize = totalSize.int64Value - freeSize.int64Value
                return Int(usedSize / 1024 / 1024)
            }
        } catch {
            return nil
        }
        return nil
    }
    
    private static func getNetworkType() -> String? {
        // Simplified - would need more complex reachability check for actual implementation
        if getIsOnline() {
            return "wifi" // Default assumption
        }
        return "offline"
    }
    
    private static func getThermalState() -> DiagnosticsThermalState? {
        switch ProcessInfo.processInfo.thermalState {
        case .nominal:
            return .nominal
        case .fair:
            return .fair
        case .serious:
            return .serious
        case .critical:
            return .critical
        @unknown default:
            return nil
        }
    }
    #endif
    
    private static func getIsOnline() -> Bool {
        #if os(iOS) || os(macOS)
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)
        
        guard let defaultRouteReachability = withUnsafePointer(to: &zeroAddress, {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }) else {
            return false
        }
        
        var flags: SCNetworkReachabilityFlags = []
        if !SCNetworkReachabilityGetFlags(defaultRouteReachability, &flags) {
            return false
        }
        
        let isReachable = flags.contains(.reachable)
        let needsConnection = flags.contains(.connectionRequired)
        
        return isReachable && !needsConnection
        #else
        return true
        #endif
    }
}

// MARK: - Connectivity Monitoring

#if os(iOS) || os(macOS)
import SystemConfiguration

/// Monitor network connectivity changes
public final class ConnectivityMonitor {
    private var reachability: SCNetworkReachability?
    private var callback: ((Bool) -> Void)?
    
    public init() {
        var zeroAddress = sockaddr_in()
        zeroAddress.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
        zeroAddress.sin_family = sa_family_t(AF_INET)
        
        reachability = withUnsafePointer(to: &zeroAddress) {
            $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                SCNetworkReachabilityCreateWithAddress(nil, $0)
            }
        }
    }
    
    public func startMonitoring(callback: @escaping (Bool) -> Void) {
        self.callback = callback
        
        guard let reachability = reachability else { return }
        
        let context = SCNetworkReachabilityContext(
            version: 0,
            info: Unmanaged.passUnretained(self).toOpaque(),
            retain: nil,
            release: nil,
            copyDescription: nil
        )
        
        SCNetworkReachabilitySetCallback(reachability, { (_, flags, info) in
            guard let info = info else { return }
            let monitor = Unmanaged<ConnectivityMonitor>.fromOpaque(info).takeUnretainedValue()
            
            let isReachable = flags.contains(.reachable)
            let needsConnection = flags.contains(.connectionRequired)
            let isConnected = isReachable && !needsConnection
            
            monitor.callback?(isConnected)
        }, &context)
        
        SCNetworkReachabilityScheduleWithRunLoop(reachability, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue)
    }
    
    public func stopMonitoring() {
        guard let reachability = reachability else { return }
        SCNetworkReachabilityUnscheduleFromRunLoop(reachability, CFRunLoopGetMain(), CFRunLoopMode.commonModes.rawValue)
    }
}
#endif
