import Darwin
import Foundation

public struct MemoryPageStatistics: Equatable, Sendable {
    public var activePages: Int64
    public var wiredPages: Int64
    public var compressedPages: Int64
    public var inactivePages: Int64
    public var freePages: Int64
    public var speculativePages: Int64
    public var totalPages: Int64

    public init(
        activePages: Int64,
        wiredPages: Int64,
        compressedPages: Int64,
        inactivePages: Int64,
        freePages: Int64,
        speculativePages: Int64,
        totalPages: Int64)
    {
        self.activePages = activePages
        self.wiredPages = wiredPages
        self.compressedPages = compressedPages
        self.inactivePages = inactivePages
        self.freePages = freePages
        self.speculativePages = speculativePages
        self.totalPages = totalPages
    }
}

public enum MemoryPressureCalculator {
    public static func pressureFraction(from statistics: MemoryPageStatistics) -> Double {
        guard statistics.totalPages > 0 else { return 0 }

        let total = Double(statistics.totalPages)
        let availablePages = max(
            0,
            statistics.freePages
                + statistics.inactivePages
                + statistics.speculativePages)
        let availableRatio = Double(availablePages) / total
        let lowAvailablePressure = 1 - min(max(availableRatio / 0.25, 0), 1)

        let compressedRatio = Double(max(statistics.compressedPages, 0)) / total
        let wiredRatio = Double(max(statistics.wiredPages, 0)) / total
        let activeRatio = Double(max(statistics.activePages, 0)) / total

        let compressionPressure = min(compressedRatio / 0.20, 1) * 0.45
        let wiredPressure = min(wiredRatio / 0.45, 1) * 0.35
        let activePressure = min(activeRatio / 0.85, 1) * 0.20

        let fraction = max(lowAvailablePressure, compressionPressure + wiredPressure + activePressure)
        return min(max(fraction, 0), 1)
    }
}

public struct MemoryPressureSnapshot: Equatable, Sendable {
    public var fraction: Double
    public var level: MemoryPressureLevel

    public init(fraction: Double, level: MemoryPressureLevel) {
        self.fraction = fraction
        self.level = level
    }
}

public struct MemoryPressureReader: Sendable {
    public init() {}

    public func read() -> MemoryPressureSnapshot {
        let statistics = Self.readPageStatistics()
        return MemoryPressureSnapshot(
            fraction: MemoryPressureCalculator.pressureFraction(from: statistics),
            level: Self.readKernelPressureLevel())
    }

    private static func readPageStatistics() -> MemoryPageStatistics {
        var stats = vm_statistics64()
        var count = mach_msg_type_number_t(
            MemoryLayout<vm_statistics64_data_t>.size / MemoryLayout<integer_t>.size)

        let result = withUnsafeMutablePointer(to: &stats) { statsPointer in
            statsPointer.withMemoryRebound(to: integer_t.self, capacity: Int(count)) { reboundPointer in
                host_statistics64(
                    mach_host_self(),
                    HOST_VM_INFO64,
                    reboundPointer,
                    &count)
            }
        }

        guard result == KERN_SUCCESS else {
            return MemoryPageStatistics(
                activePages: 0,
                wiredPages: 0,
                compressedPages: 0,
                inactivePages: 0,
                freePages: 0,
                speculativePages: 0,
                totalPages: 0)
        }

        let totalBytes = ProcessInfo.processInfo.physicalMemory
        let pageSize = UInt64(max(sysconf(_SC_PAGESIZE), 0))
        let totalPages = pageSize == 0 ? 0 : Int64(totalBytes / pageSize)

        return MemoryPageStatistics(
            activePages: Int64(stats.active_count),
            wiredPages: Int64(stats.wire_count),
            compressedPages: Int64(stats.compressor_page_count),
            inactivePages: Int64(stats.inactive_count),
            freePages: Int64(stats.free_count),
            speculativePages: Int64(stats.speculative_count),
            totalPages: totalPages)
    }

    private static func readKernelPressureLevel() -> MemoryPressureLevel {
        var value: Int32 = 0
        var size = MemoryLayout<Int32>.size
        let result = sysctlbyname("kern.memorystatus_vm_pressure_level", &value, &size, nil, 0)
        guard result == 0 else { return .normal }
        return MemoryPressureLevel(kernelValue: value)
    }
}
