import Foundation
import os

// MARK: - Memory Monitor
// Monitors system memory pressure and provides adaptive behavior

/// Memory pressure levels
public enum MemoryPressureLevel {
    case normal
    case warning
    case critical

    var description: String {
        switch self {
        case .normal: return "Normal"
        case .warning: return "Warning - Memory pressure detected"
        case .critical: return "Critical - Low memory"
        }
    }
}

/// Memory statistics
public struct MemoryStats {
    public let available: UInt64        // Available memory in bytes
    public let used: UInt64             // Used memory in bytes
    public let total: UInt64            // Total physical memory
    public let pressureLevel: MemoryPressureLevel

    public var availableMB: Double {
        return Double(available) / (1024.0 * 1024.0)
    }

    public var usedMB: Double {
        return Double(used) / (1024.0 * 1024.0)
    }

    public var totalMB: Double {
        return Double(total) / (1024.0 * 1024.0)
    }

    public var usagePercentage: Double {
        guard total > 0 else { return 0 }
        return (Double(used) / Double(total)) * 100.0
    }
}

/// Memory monitoring and management
public class MemoryMonitor {

    // MARK: - Properties

    public static let shared = MemoryMonitor()

    private var observers: [UUID: (MemoryPressureLevel) -> Void] = [:]
    private let observerQueue = DispatchQueue(label: "com.imageSDK.memorymonitor", attributes: .concurrent)

    // Memory thresholds (in MB)
    private let warningThreshold: Double = 200.0    // 200 MB available
    private let criticalThreshold: Double = 100.0   // 100 MB available

    // Dispatch source for memory pressure monitoring
    private var memoryPressureSource: DispatchSourceMemoryPressure?
    private var isMonitoring = false

    // MARK: - Initialization

    private init() {
        setupMemoryPressureMonitoring()
    }

    // MARK: - Public Methods

    /// Get current memory statistics
    public func getMemoryStats() -> MemoryStats {
        var stats = 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: &stats) {
            $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
                task_info(mach_task_self_,
                         task_flavor_t(MACH_TASK_BASIC_INFO),
                         $0,
                         &count)
            }
        }

        let used = kerr == KERN_SUCCESS ? UInt64(stats.resident_size) : 0

        // Get physical memory
        let total = ProcessInfo.processInfo.physicalMemory

        // Estimate available (this is approximate)
        let available = total > used ? total - used : 0

        // Determine pressure level
        let availableMB = Double(available) / (1024.0 * 1024.0)
        let pressureLevel: MemoryPressureLevel
        if availableMB < criticalThreshold {
            pressureLevel = .critical
        } else if availableMB < warningThreshold {
            pressureLevel = .warning
        } else {
            pressureLevel = .normal
        }

        return MemoryStats(
            available: available,
            used: used,
            total: total,
            pressureLevel: pressureLevel
        )
    }

    /// Add observer for memory pressure changes
    /// - Parameter callback: Called when memory pressure changes
    /// - Returns: UUID token to remove observer later
    @discardableResult
    public func addObserver(_ callback: @escaping (MemoryPressureLevel) -> Void) -> UUID {
        let id = UUID()
        observerQueue.async(flags: .barrier) {
            self.observers[id] = callback
        }
        return id
    }

    /// Remove observer
    /// - Parameter id: UUID token from addObserver
    public func removeObserver(_ id: UUID) {
        observerQueue.async(flags: .barrier) {
            self.observers.removeValue(forKey: id)
        }
    }

    /// Start monitoring memory pressure
    public func startMonitoring() {
        guard !isMonitoring else { return }
        isMonitoring = true

        memoryPressureSource = DispatchSource.makeMemoryPressureSource(
            eventMask: [.warning, .critical],
            queue: .global(qos: .utility)
        )

        memoryPressureSource?.setEventHandler { [weak self] in
            guard let self = self else { return }

            let stats = self.getMemoryStats()
            self.notifyObservers(pressureLevel: stats.pressureLevel)
        }

        memoryPressureSource?.resume()
    }

    /// Stop monitoring memory pressure
    public func stopMonitoring() {
        guard isMonitoring else { return }
        isMonitoring = false
        memoryPressureSource?.cancel()
        memoryPressureSource = nil
    }

    /// Check if current memory conditions are suitable for operation
    /// - Parameter requiredMB: Minimum required memory in MB
    /// - Returns: true if operation should proceed
    public func canPerformOperation(requiredMB: Double) -> Bool {
        let stats = getMemoryStats()
        return stats.availableMB >= requiredMB
    }

    /// Calculate adaptive history depth based on image size and available memory
    /// - Parameters:
    ///   - imageWidth: Image width in pixels
    ///   - imageHeight: Image height in pixels
    ///   - defaultDepth: Default history depth
    /// - Returns: Recommended history depth
    public func adaptiveHistoryDepth(imageWidth: Int, imageHeight: Int, defaultDepth: Int = 50) -> Int {
        let stats = getMemoryStats()

        // Estimate memory per state (rough calculation)
        // Assuming RGBA format: width * height * 4 bytes
        let pixelCount = imageWidth * imageHeight
        let memoryPerStateMB = Double(pixelCount * 4) / (1024.0 * 1024.0)

        // Calculate how many states we can afford
        // Use 30% of available memory for history
        let availableForHistory = stats.availableMB * 0.3
        let affordableStates = Int(availableForHistory / memoryPerStateMB)

        // Clamp to reasonable range
        let minStates = 5
        let maxStates = defaultDepth

        return max(minStates, min(affordableStates, maxStates))
    }

    /// Get recommended preview scale based on image size and memory
    /// - Parameters:
    ///   - imageWidth: Image width in pixels
    ///   - imageHeight: Image height in pixels
    ///   - maxPreviewDimension: Maximum preview dimension
    /// - Returns: Recommended scale factor (0.0 to 1.0)
    public func recommendedPreviewScale(imageWidth: Int, imageHeight: Int, maxPreviewDimension: Int = 2048) -> CGFloat {
        let stats = getMemoryStats()

        // If memory is critical, use lower resolution
        switch stats.pressureLevel {
        case .critical:
            let maxDim = max(imageWidth, imageHeight)
            return CGFloat(min(1024, maxDim)) / CGFloat(maxDim)
        case .warning:
            let maxDim = max(imageWidth, imageHeight)
            return CGFloat(min(1536, maxDim)) / CGFloat(maxDim)
        case .normal:
            let maxDim = max(imageWidth, imageHeight)
            if maxDim > maxPreviewDimension {
                return CGFloat(maxPreviewDimension) / CGFloat(maxDim)
            }
            return 1.0
        }
    }

    // MARK: - Private Methods

    private func setupMemoryPressureMonitoring() {
        // Automatically start monitoring
        startMonitoring()

        // Add notification observer for app lifecycle
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(handleMemoryWarning),
            name: UIApplication.didReceiveMemoryWarningNotification,
            object: nil
        )
    }

    @objc private func handleMemoryWarning() {
        notifyObservers(pressureLevel: .critical)
    }

    private func notifyObservers(pressureLevel: MemoryPressureLevel) {
        observerQueue.sync {
            observers.values.forEach { callback in
                DispatchQueue.main.async {
                    callback(pressureLevel)
                }
            }
        }
    }

    deinit {
        stopMonitoring()
        NotificationCenter.default.removeObserver(self)
    }
}

// MARK: - Memory-Aware Cache Protocol

/// Protocol for caches that can respond to memory pressure
public protocol MemoryAwareCache {
    func clearCacheOnMemoryPressure(level: MemoryPressureLevel)
}

// MARK: - Helper Extensions

extension MemoryMonitor {
    /// Format bytes to human-readable string
    public static func formatBytes(_ bytes: UInt64) -> String {
        let formatter = ByteCountFormatter()
        formatter.allowedUnits = [.useAll]
        formatter.countStyle = .memory
        return formatter.string(fromByteCount: Int64(bytes))
    }

    /// Log current memory stats
    public func logMemoryStats() {
        let stats = getMemoryStats()
        print("""
        📊 Memory Stats:
        - Available: \(String(format: "%.1f", stats.availableMB)) MB
        - Used: \(String(format: "%.1f", stats.usedMB)) MB
        - Total: \(String(format: "%.1f", stats.totalMB)) MB
        - Usage: \(String(format: "%.1f", stats.usagePercentage))%
        - Pressure: \(stats.pressureLevel.description)
        """)
    }
}
