import SwiftUI

// MARK: - Call Bar Safe Area Bridge

/// UIKit bridge that pushes NavigationStack's nav bar down by setting
/// `additionalSafeAreaInsets.top` on every UINavigationController in the window.
///
/// `UIViewController.navigationController` walks UP the parent chain — but the
/// bridge VC is a SIBLING of NavigationStack's UINavigationController, not a child.
/// So we walk DOWN from the window's root VC to find all navigation controllers.
struct CallBarSafeAreaBridge: UIViewRepresentable {
    let topInset: CGFloat

    func makeUIView(context: Context) -> CallBarInsetView {
        let view = CallBarInsetView()
        view.isHidden = true
        view.isUserInteractionEnabled = false
        return view
    }

    func updateUIView(_ view: CallBarInsetView, context: Context) {
        view.topInset = topInset
        view.applyInset()
    }
}

/// Invisible UIView that finds all UINavigationControllers in the window
/// and sets `additionalSafeAreaInsets.top` on each.
final class CallBarInsetView: UIView {
    var topInset: CGFloat = 0
    private var lastAppliedInset: CGFloat = -1

    override func didMoveToWindow() {
        super.didMoveToWindow()
        lastAppliedInset = -1 // Force re-apply
        applyInset()
    }

    func applyInset() {
        guard topInset != lastAppliedInset else { return }
        guard let window, let rootVC = window.rootViewController else { return }
        let inset = topInset
        // Animate in sync with SwiftUI spring transition to avoid jerk.
        // layoutIfNeeded() forces the layout pass INSIDE the animation block —
        // without it, additionalSafeAreaInsets triggers layout on the next
        // run loop pass, which is outside the animation scope → instant jump.
        UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.85,
                       initialSpringVelocity: 0, options: [.beginFromCurrentState]) {
            Self.apply(inset: inset, in: rootVC)
            rootVC.view.layoutIfNeeded()
        }
        lastAppliedInset = topInset
    }

    /// Recursively walk child view controllers (NOT presentedViewController)
    /// and set additionalSafeAreaInsets.top on every UINavigationController.
    private static func apply(inset: CGFloat, in vc: UIViewController) {
        if let nav = vc as? UINavigationController,
           nav.additionalSafeAreaInsets.top != inset {
            nav.additionalSafeAreaInsets.top = inset
        }
        for child in vc.children {
            apply(inset: inset, in: child)
        }
    }
}

extension View {
    /// Push NavigationStack's nav bar down by `inset` points.
    /// Uses UIKit `additionalSafeAreaInsets` — the only reliable mechanism.
    func callBarSafeAreaInset(_ inset: CGFloat) -> some View {
        background(
            CallBarSafeAreaBridge(topInset: inset)
                .frame(width: 0, height: 0)
                .allowsHitTesting(false)
        )
    }
}
