//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

/// Statistics for a container.
public struct ContainerStatistics: Sendable {
    public var id: String
    public var process: ProcessStatistics?
    public var memory: MemoryStatistics?
    public var cpu: CPUStatistics?
    public var blockIO: BlockIOStatistics?
    public var networks: [NetworkStatistics]?
    public var memoryEvents: MemoryEventStatistics?

    public init(
        id: String,
        process: ProcessStatistics? = nil,
        memory: MemoryStatistics? = nil,
        cpu: CPUStatistics? = nil,
        blockIO: BlockIOStatistics? = nil,
        networks: [NetworkStatistics]? = nil,
        memoryEvents: MemoryEventStatistics? = nil
    ) {
        self.id = id
        self.process = process
        self.memory = memory
        self.cpu = cpu
        self.blockIO = blockIO
        self.networks = networks
        self.memoryEvents = memoryEvents
    }

    /// Process statistics for a container.
    public struct ProcessStatistics: Sendable {
        public var current: UInt64
        public var limit: UInt64

        public init(current: UInt64, limit: UInt64) {
            self.current = current
            self.limit = limit
        }
    }

    /// Memory statistics for a container.
    public struct MemoryStatistics: Sendable {
        public var usageBytes: UInt64
        public var limitBytes: UInt64
        public var swapUsageBytes: UInt64
        public var swapLimitBytes: UInt64
        public var cacheBytes: UInt64
        public var kernelStackBytes: UInt64
        public var slabBytes: UInt64
        public var pageFaults: UInt64
        public var majorPageFaults: UInt64
        public var inactiveFile: UInt64
        public var anon: UInt64
        public var workingsetRefaultAnon: UInt64
        public var workingsetRefaultFile: UInt64
        public var pgstealKswapd: UInt64
        public var pgstealDirect: UInt64
        public var pgstealKhugepaged: UInt64

        public init(
            usageBytes: UInt64,
            limitBytes: UInt64,
            swapUsageBytes: UInt64,
            swapLimitBytes: UInt64,
            cacheBytes: UInt64,
            kernelStackBytes: UInt64,
            slabBytes: UInt64,
            pageFaults: UInt64,
            majorPageFaults: UInt64,
            inactiveFile: UInt64,
            anon: UInt64,
            workingsetRefaultAnon: UInt64 = 0,
            workingsetRefaultFile: UInt64 = 0,
            pgstealKswapd: UInt64 = 0,
            pgstealDirect: UInt64 = 0,
            pgstealKhugepaged: UInt64 = 0
        ) {
            self.usageBytes = usageBytes
            self.limitBytes = limitBytes
            self.swapUsageBytes = swapUsageBytes
            self.swapLimitBytes = swapLimitBytes
            self.cacheBytes = cacheBytes
            self.kernelStackBytes = kernelStackBytes
            self.slabBytes = slabBytes
            self.pageFaults = pageFaults
            self.majorPageFaults = majorPageFaults
            self.inactiveFile = inactiveFile
            self.anon = anon
            self.workingsetRefaultAnon = workingsetRefaultAnon
            self.workingsetRefaultFile = workingsetRefaultFile
            self.pgstealKswapd = pgstealKswapd
            self.pgstealDirect = pgstealDirect
            self.pgstealKhugepaged = pgstealKhugepaged
        }
    }

    /// CPU statistics for a container.
    public struct CPUStatistics: Sendable {
        public var usageUsec: UInt64
        public var userUsec: UInt64
        public var systemUsec: UInt64
        public var throttlingPeriods: UInt64
        public var throttledPeriods: UInt64
        public var throttledTimeUsec: UInt64

        public init(
            usageUsec: UInt64,
            userUsec: UInt64,
            systemUsec: UInt64,
            throttlingPeriods: UInt64,
            throttledPeriods: UInt64,
            throttledTimeUsec: UInt64
        ) {
            self.usageUsec = usageUsec
            self.userUsec = userUsec
            self.systemUsec = systemUsec
            self.throttlingPeriods = throttlingPeriods
            self.throttledPeriods = throttledPeriods
            self.throttledTimeUsec = throttledTimeUsec
        }
    }

    /// Block I/O statistics for a container.
    public struct BlockIOStatistics: Sendable {
        public var devices: [BlockIODevice]

        public init(devices: [BlockIODevice]) {
            self.devices = devices
        }
    }

    /// Block I/O statistics for a specific device.
    public struct BlockIODevice: Sendable {
        public var major: UInt64
        public var minor: UInt64
        public var readBytes: UInt64
        public var writeBytes: UInt64
        public var readOperations: UInt64
        public var writeOperations: UInt64

        public init(
            major: UInt64,
            minor: UInt64,
            readBytes: UInt64,
            writeBytes: UInt64,
            readOperations: UInt64,
            writeOperations: UInt64
        ) {
            self.major = major
            self.minor = minor
            self.readBytes = readBytes
            self.writeBytes = writeBytes
            self.readOperations = readOperations
            self.writeOperations = writeOperations
        }
    }

    /// Statistics for a network interface.
    public struct NetworkStatistics: Sendable {
        public var interface: String
        public var receivedPackets: UInt64
        public var transmittedPackets: UInt64
        public var receivedBytes: UInt64
        public var transmittedBytes: UInt64
        public var receivedErrors: UInt64
        public var transmittedErrors: UInt64

        public init(
            interface: String,
            receivedPackets: UInt64,
            transmittedPackets: UInt64,
            receivedBytes: UInt64,
            transmittedBytes: UInt64,
            receivedErrors: UInt64,
            transmittedErrors: UInt64
        ) {
            self.interface = interface
            self.receivedPackets = receivedPackets
            self.transmittedPackets = transmittedPackets
            self.receivedBytes = receivedBytes
            self.transmittedBytes = transmittedBytes
            self.receivedErrors = receivedErrors
            self.transmittedErrors = transmittedErrors
        }
    }

    /// Memory event counters from cgroup2's memory.events file.
    public struct MemoryEventStatistics: Sendable {
        /// Number of times the cgroup was reclaimed due to low memory.
        public var low: UInt64
        /// Number of times the cgroup exceeded its high memory limit.
        public var high: UInt64
        /// Number of times the cgroup hit its max memory limit.
        public var max: UInt64
        /// Number of times the cgroup triggered OOM.
        public var oom: UInt64
        /// Number of processes killed by OOM killer.
        public var oomKill: UInt64

        public init(low: UInt64, high: UInt64, max: UInt64, oom: UInt64, oomKill: UInt64) {
            self.low = low
            self.high = high
            self.max = max
            self.oom = oom
            self.oomKill = oomKill
        }
    }
}

/// Categories of statistics that can be requested.
public struct StatCategory: OptionSet, Sendable {
    public let rawValue: Int

    public init(rawValue: Int) {
        self.rawValue = rawValue
    }

    /// Process statistics (pids.current, pids.max).
    public static let process = StatCategory(rawValue: 1 << 0)
    /// Memory usage statistics.
    public static let memory = StatCategory(rawValue: 1 << 1)
    /// CPU usage statistics.
    public static let cpu = StatCategory(rawValue: 1 << 2)
    /// Block I/O statistics.
    public static let blockIO = StatCategory(rawValue: 1 << 3)
    /// Network interface statistics.
    public static let network = StatCategory(rawValue: 1 << 4)
    /// Memory event counters (OOM kills, pressure events, etc.).
    public static let memoryEvents = StatCategory(rawValue: 1 << 5)

    /// All available statistics categories.
    public static let all: StatCategory = [.process, .memory, .cpu, .blockIO, .network, .memoryEvents]
}
