//
//  LNNestedScrollView.swift
//  Gami
//
//  Created by OneeChan on 2026/3/12.
//

import Foundation
import UIKit


private class ScrollViewWeakWrapper {
    weak var delegate: LNNestedScrollViewDelegate?
    weak var childListView: UIScrollView?
}

private var scrollViewWeakWrapperKey: UInt8 = 0
private extension UIScrollView {
    var minTopOffset: CGFloat {
        contentSize.height - bounds.height - contentInset.top + contentInset.bottom
    }
    var weakWrapper: ScrollViewWeakWrapper {
        get {
            var wrapper = objc_getAssociatedObject(self, &scrollViewWeakWrapperKey)
                as? ScrollViewWeakWrapper
            if wrapper == nil {
                wrapper = ScrollViewWeakWrapper()
                objc_setAssociatedObject(
                    self,
                    &scrollViewWeakWrapperKey,
                    wrapper,
                    .OBJC_ASSOCIATION_RETAIN_NONATOMIC
                )
            }
            return wrapper!
        }
        set {
            objc_setAssociatedObject(
                self,
                &scrollViewWeakWrapperKey,
                newValue,
                .OBJC_ASSOCIATION_RETAIN_NONATOMIC
            )
        }
    }
    
    weak var curChildListView: UIScrollView? {
        get { weakWrapper.childListView }
        set { weakWrapper.childListView = newValue }
    }
}

extension UIScrollView: LNNestedScrollViewDelegate {
    func listViewDidScroll(_ scrollView: UIScrollView) -> Bool {
        curChildListView = scrollView
        
        if contentOffset.y < minTopOffset - 0.001 {
            return true
        }
        
        contentOffset.y = minTopOffset
        return false
    }
}

extension UIScrollView {
    weak var observerDelegate: LNNestedScrollViewDelegate? {
        get { weakWrapper.delegate }
        set { weakWrapper.delegate = newValue }
    }
}


protocol LNNestedScrollViewDelegate: AnyObject {
    func listViewDidScroll(_ scrollView: UIScrollView) -> Bool
}


private class ScrollViewDelegateProxy: NSObject, UIScrollViewDelegate {
    weak var originalDelegate: UIScrollViewDelegate?
    weak var observerDelegate: UIScrollViewDelegate?
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        originalDelegate?.scrollViewDidScroll?(scrollView)
        observerDelegate?.scrollViewDidScroll?(scrollView)
    }
    
    override func forwardingTarget(for aSelector: Selector!) -> Any? {
        if originalDelegate?.responds(to: aSelector) == true {
            return originalDelegate
        }
        return super.forwardingTarget(for: aSelector)
    }
    
    override func responds(to aSelector: Selector!) -> Bool {
        return super.responds(to: aSelector) || originalDelegate?.responds(to: aSelector) == true
    }
}


class LNNestedScrollView: UIScrollView, UIScrollViewDelegate, UIGestureRecognizerDelegate {
    weak var scrollDelegate: UIScrollViewDelegate? {
        didSet {
            proxy.originalDelegate = scrollDelegate
            delegate = nil
            delegate = proxy
        }
    }
    private var proxy = ScrollViewDelegateProxy()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        proxy.originalDelegate = scrollDelegate
        proxy.observerDelegate = self
        delegate = proxy
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if observerDelegate?.listViewDidScroll(self) == true {
            scrollView.contentOffset.y = -contentInset.top
        }
        
        if let curChildListView,
            curChildListView.contentOffset.y > -curChildListView.contentInset.top {
            scrollView.contentOffset.y = minTopOffset
        }
    }
    
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        // 禁止水平滚动的 ScrollView 手势同时响应
        if let scrollView = gestureRecognizer.view as? UIScrollView,
           scrollView.contentSize.height == scrollView.bounds.height {
            return false
        }
        if let scrollView = otherGestureRecognizer.view as? UIScrollView,
           scrollView.contentSize.height == scrollView.bounds.height {
            return false
        }
        
        return gestureRecognizer.isKind(of: UIPanGestureRecognizer.self) && otherGestureRecognizer.isKind(of: UIPanGestureRecognizer.self)
    }
}

private class TableViewDelegateProxy: NSObject, UITableViewDelegate {
    weak var originalDelegate: UITableViewDelegate?
    weak var observerDelegate: UITableViewDelegate?
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        originalDelegate?.scrollViewDidScroll?(scrollView)
        observerDelegate?.scrollViewDidScroll?(scrollView)
    }
    
    override func forwardingTarget(for aSelector: Selector!) -> Any? {
        if originalDelegate?.responds(to: aSelector) == true {
            return originalDelegate
        }
        return super.forwardingTarget(for: aSelector)
    }
    
    override func responds(to aSelector: Selector!) -> Bool {
        super.responds(to: aSelector)
            || originalDelegate?.responds(to: aSelector) == true
    }
}


class LNNestedTableView: UITableView, UITableViewDelegate, UIGestureRecognizerDelegate {
    weak var tableDelegate: UITableViewDelegate? {
        didSet {
            proxy.originalDelegate = tableDelegate
            delegate = nil
            delegate = proxy
        }
    }
    private var proxy = TableViewDelegateProxy()
    
    override init(frame: CGRect, style: UITableView.Style) {
        super.init(frame: frame, style: style)
        
        proxy.originalDelegate = tableDelegate
        proxy.observerDelegate = self
        delegate = proxy
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if observerDelegate?.listViewDidScroll(self) == true {
            scrollView.contentOffset.y = -contentInset.top
        }
        
        
        if let curChildListView,
            curChildListView.contentOffset.y > -curChildListView.contentInset.top {
            scrollView.contentOffset.y = minTopOffset
        }
    }
    
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        // 禁止水平滚动的 ScrollView 手势同时响应
        if let scrollView = gestureRecognizer.view as? UIScrollView,
           scrollView.contentSize.height == scrollView.bounds.height {
            return false
        }
        if let scrollView = otherGestureRecognizer.view as? UIScrollView,
           scrollView.contentSize.height == scrollView.bounds.height {
            return false
        }
        
        return gestureRecognizer.isKind(of: UIPanGestureRecognizer.self) && otherGestureRecognizer.isKind(of: UIPanGestureRecognizer.self)
    }
}
