import SwiftUI
import SwiftUIIntrospect
import ObjectiveC

struct NavigationSwipeModifier: ViewModifier {
    func body(content: Content) -> some View {
        content
            .introspect(.tabView(style: .page), on: .iOS(.v18, .v26)) { collectionView in
                enableSwipeBack(for: collectionView)
            }
    }

    private func enableSwipeBack(for collectionView: UICollectionView) {
        guard let navController = sequence(first: collectionView as UIResponder?, next: { $0?.next })
            .first(where: { $0 is UINavigationController }) as? UINavigationController,
              let popGestureRecognizer = navController.interactivePopGestureRecognizer,
              let targets = popGestureRecognizer.value(forKey: "targets") as? NSMutableArray else {
            return
        }

        let panGesture = UIPanGestureRecognizer()
        panGesture.setValue(targets, forKey: "targets")

        let delegate = NavigationSwipeGestureDelegate(collectionView: collectionView, panGesture: panGesture)
        panGesture.delegate = delegate

        objc_setAssociatedObject(collectionView, "navigationSwipeDelegate", delegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)

        collectionView.addGestureRecognizer(panGesture)
    }
}

private class NavigationSwipeGestureDelegate: NSObject, UIGestureRecognizerDelegate {
    weak var collectionView: UICollectionView?
    weak var panGesture: UIPanGestureRecognizer?

    init(collectionView: UICollectionView, panGesture: UIPanGestureRecognizer) {
        self.collectionView = collectionView
        self.panGesture = panGesture
    }

    func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard let panRecognizer = gestureRecognizer as? UIPanGestureRecognizer,
              panRecognizer == panGesture,
              let view = panRecognizer.view else {
            return true
        }

        let velocityX = panRecognizer.velocity(in: view).x
        let translationX = panRecognizer.translation(in: view).x
        let isAtLeftEdge = collectionView?.contentOffset.x ?? 0 <= 0

        return velocityX > 0 && translationX > 0 && isAtLeftEdge
    }

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer,
                           shouldBeRequiredToFailBy otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        if gestureRecognizer == panGesture,
           otherGestureRecognizer == collectionView?.panGestureRecognizer {
            return true
        }
        return false
    }
}

public extension TabView {
    /// Enables navigation swipe-to-go-back when using `.tabViewStyle(.page)` when the first tab is active.
    /// This is a workaround for the lack of native support for swipe back gestures in paged TabViews.
    func navigationSwipeEnabled() -> some View {
        modifier(NavigationSwipeModifier())
    }
}
