//
//  UIScrollView+Extension.swift
//  Gami
//
//  Created by OneeChan on 2026/1/25.
//

import Foundation
import UIKit

private var scrollViewOriginContentInsetKey: UInt8 = 0

extension UIScrollView: LNKeyboardNotify {
    private var originContentInset: UIEdgeInsets {
        get {
            objc_getAssociatedObject(self, &scrollViewOriginContentInsetKey) as? UIEdgeInsets ?? .zero
        }
        set {
            objc_setAssociatedObject(self, &scrollViewOriginContentInsetKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
        }
    }
    
    func adjustKeyoard() {
        LNEventDeliver.addObserver(self)
    }
    
    func onKeyboardWillShow(curInput: UIView?, keyboardHeight: CGFloat) {
        guard let curInput, curInput.isDescendant(of: self) else { return }
        
        originContentInset = contentInset
        
        let offset = 20 + keyboardHeight
        
        let editViewY = curInput.convert(curInput.bounds, to: nil).maxY
        let distance = UIScreen.main.bounds.height - editViewY
        if distance > offset {
            return
        }
        
        var remain = offset - distance
        let curOffset = contentOffset
        let availableOffset = contentSize.height + contentInset.bottom - curOffset.y - bounds.height
        if availableOffset > remain {
            setContentOffset(.init(x: curOffset.x, y: curOffset.y + remain), animated: true)
            return
        }
        remain = remain - availableOffset
        
        
        var curInset = contentInset
        curInset.bottom += remain
        contentInset = curInset
        
        setContentOffset(.init(x: curOffset.x, y: curOffset.y + availableOffset + remain), animated: true)
    }
    
    func onKeyboardWillHide(curInput: UIView?) {
        guard let curInput, curInput.isDescendant(of: self) else { return }
        
        contentInset = originContentInset
    }
}

extension UIScrollView: UIGestureRecognizerDelegate {
    func toBeNested() {
        panGestureRecognizer.delegate = self
        bounces = false
    }
    
    // MARK: - UIGestureRecognizerDelegate
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return true
    }
    
    open override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer,
              let currentScrollView = panGesture.view as? UIScrollView else {
            return true
        }
        
        guard currentScrollView.isGestureWithinSelfBounds(panGesture) else {
            return false // 不在范围内，直接忽略手势
        }
        
        // 获取滑动方向（向上：y 负，向下：y 正）
        let translation = panGesture.translation(in: currentScrollView)
        let isScrollingUp = translation.y < 0 // 向上滑
        let isScrollingDown = translation.y > 0 // 向下滑
        
        // 根据滑动方向判断当前 ScrollView 是否可以响应手势
        if isScrollingUp {
            return currentScrollView.checkOuterScrollViewsAtBottom(panGesture: panGesture)
        } else if isScrollingDown {
            return currentScrollView.checkInnerScrollViewsAtTop(panGesture: panGesture)
        }
        
        return true
    }
    
    // MARK: - UI 树遍历核心逻辑
    /// 上滑：检查所有外层 ScrollView 是否都滚到底部
    private func checkOuterScrollViewsAtBottom(panGesture: UIPanGestureRecognizer) -> Bool {
        var currentSuperview = self.superview
        // 向上遍历父视图，找所有外层 ScrollView
        while let superview = currentSuperview {
            if let outerScrollView = superview as? UIScrollView {
                // 只要有一个外层没滚到底，当前 ScrollView 就不能响应
                if !outerScrollView.isAtBottom() {
                    return false
                }
            }
            currentSuperview = superview.superview
        }
        // 所有外层都滚到底，当前可以响应
        return !isAtBottom()
    }
    
    /// 下滑：检查所有内层 ScrollView 是否都滚到顶部
    private func checkInnerScrollViewsAtTop(panGesture: UIPanGestureRecognizer) -> Bool {
        // 向下遍历子视图，找所有内层 ScrollView
        // 递归遍历子视图的子视图（深层嵌套）
        if findFirstNotAtTopScrollView(in: self, panGesture: panGesture) != nil {
            return false
        }
        // 所有内层都滚到顶，当前可以响应
        return !isAtTop()
    }
    
    /// 递归查找子视图中的第一个 ScrollView（处理深层嵌套）
    private func findFirstNotAtTopScrollView(
        in view: UIView,
        panGesture: UIPanGestureRecognizer) -> UIScrollView? {
        for subview in view.subviews {
            if let scrollView = subview as? UIScrollView,
                scrollView.isGestureWithinSelfBounds(panGesture),
                !scrollView.isAtTop() {
                return scrollView
            }
            if let found = findFirstNotAtTopScrollView(in: subview, panGesture: panGesture) {
                return found
            }
        }
        return nil
    }
    
    // MARK: - 滚动边界判断
    private func isAtTop() -> Bool {
        let topOffset = contentOffset.y + adjustedContentInset.top
        return topOffset <= 1 // 允许1pt误差
    }
    
    private func isAtBottom() -> Bool {
        let contentHeight = contentSize.height + adjustedContentInset.bottom
        let bottomOffset = contentOffset.y + bounds.height
        return bottomOffset >= contentHeight - 1
    }
    
    /// 判断手势触摸点是否在当前 ScrollView 的可视范围内
    private func isGestureWithinSelfBounds(_ panGesture: UIPanGestureRecognizer) -> Bool {
        // 1. 获取手势的触摸点（相对于当前 ScrollView）
        let touchLocation = panGesture.location(in: self)
        // 2. 转换为 ScrollView 的 bounds 坐标系（处理 contentOffset 和 contentInset）
        let convertedPoint = CGPoint(
            x: touchLocation.x - adjustedContentInset.left,
            y: touchLocation.y - adjustedContentInset.top
        )
        // 3. 校验点是否在 ScrollView 的可视区域内（排除超出 bounds 的部分）
        return bounds.contains(convertedPoint)
    }
}
