import ComposableArchitecture
import Foundation

/// Prints the current stack usage
/// https://forums.swift.org/t/getting-stack-size-using-lldb/63207/4
public func checkStack(_ param: Any = "nil") {
    var x: UInt8 = 1
    func approximateSP(_ p: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer {
        p
    }
    let sp = approximateSP(&x)
    let top = pthread_get_stackaddr_np(pthread_self())
    let size = pthread_get_stacksize_np(pthread_self())
    let bottom = top - size
    let safetySize = min(10 * 1024, size / 10)
    let safeBottom = bottom + safetySize // "relatively" safe stack bottom
    print("top    : \(top)")
    print("SP     : \(sp) (approximate)")
    print("safe   : \(safeBottom) (relatively safe bottom)")
    print("bottom : \(bottom)")
    print("size   : \(size)")
    print("used   : \(top - sp) [\(100 * (top - sp) / size)%]")
    print("param  : \(param)")
    print()

    precondition(sp > safeBottom && sp <= top, "stack is about to overflow \(sp - bottom) bytes left, depth: \(param)")
}

/// Helper to print stack on Reducer passes
public extension Reducer {
    /// Usage:
    /// ```
    /// Reduce { state, action in
    /// .... your reducer actions
    /// }
    /// ._checkStack("optional description for debug console")
    /// ```
    @inlinable
    @warn_unqualified_access
    func _checkStack(_ param: Any = "nil") -> Self {
        checkStack(param)
        return self
    }
}
