//
//  YHBottomPresentationController.swift
//  galaxy
//
//  Created by alexzzw on 2024/10/9.
//  Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//

import UIKit

class YHBottomPresentationController: UIPresentationController {
    private var dimmingView: UIView?
    private var presentationWrappingView: UIView?
    public var customRadius: CGFloat = 8.0
    var blurEffectAlpha: CGFloat = 0.5
    var dimmingViewTapAction: (() -> Void)?
    private lazy var visualEffectView: UIVisualEffectView = {
        let effect = UIBlurEffect(style: .dark)
        let view = UIVisualEffectView(effect: effect)
        view.alpha = 0.5
        return view
    }()

    override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
        presentedViewController.modalPresentationStyle = .custom
    }

    override var presentedView: UIView? {
        return presentationWrappingView
    }

    override func presentationTransitionWillBegin() {
        let presentationWrapperView = UIView(frame: frameOfPresentedViewInContainerView)
        presentationWrapperView.layer.shadowOpacity = 0.44
        presentationWrapperView.layer.shadowRadius = 13.0
        presentationWrapperView.layer.shadowOffset = CGSize(width: 0, height: -6)
        presentationWrappingView = presentationWrapperView
        let presentationRoundedCornerView = UIView(frame: presentationWrapperView.bounds.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: -customRadius, right: 0)))
        presentationRoundedCornerView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
        presentationRoundedCornerView.layer.cornerRadius = customRadius
        presentationRoundedCornerView.layer.masksToBounds = true

        let presentedViewControllerWrapperView = UIView(frame: presentationRoundedCornerView.bounds.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: customRadius, right: 0)))
        presentedViewControllerWrapperView.autoresizingMask = [.flexibleHeight, .flexibleWidth]

        let presentedViewControllerView = super.presentedView
        if let presentedViewControllerView = presentedViewControllerView {
            presentedViewControllerView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
            presentedViewControllerView.frame = presentedViewControllerWrapperView.bounds
            presentedViewControllerWrapperView.addSubview(presentedViewControllerView)
        }
        presentationRoundedCornerView.addSubview(presentedViewControllerWrapperView)
        presentationWrapperView.addSubview(presentationRoundedCornerView)

        if let containerView = containerView {
            let dimmingView = UIView(frame: containerView.bounds)
            visualEffectView.frame = dimmingView.bounds
            visualEffectView.alpha = blurEffectAlpha
            dimmingView.addSubview(visualEffectView)
            visualEffectView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
            dimmingView.backgroundColor = UIColor.black.withAlphaComponent(blurEffectAlpha)
            dimmingView.isOpaque = false
            dimmingView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
            dimmingView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dimmingViewTapped(_:))))
            self.dimmingView = dimmingView
            containerView.addSubview(dimmingView)
            let transitionCoordinator = presentingViewController.transitionCoordinator
            dimmingView.alpha = 0.0
            transitionCoordinator?.animate(alongsideTransition: { [weak self] _ in
                self?.dimmingView?.alpha = 1
            }, completion: nil)
        }
    }

    override func presentationTransitionDidEnd(_ completed: Bool) {
        if !completed {
            presentationWrappingView = nil
            dimmingView = nil
        }
    }

    override func dismissalTransitionWillBegin() {
        let transitionCoordinator = presentingViewController.transitionCoordinator
        transitionCoordinator?.animate(alongsideTransition: { [weak self] _ in
            self?.dimmingView?.alpha = 0
        }, completion: nil)
    }

    override func dismissalTransitionDidEnd(_ completed: Bool) {
        if completed {
            presentationWrappingView = nil
            dimmingView = nil
        }
    }

    // MARK: Layout

    override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
        super.preferredContentSizeDidChange(forChildContentContainer: container)
        if container === presentedViewController {
            containerView?.setNeedsLayout()
        }
    }

    override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize {
        if container === presentedViewController, (container as? UIViewController) != nil {
            return container.preferredContentSize
        } else {
            return super.size(forChildContentContainer: container, withParentContainerSize: parentSize)
        }
    }

    override var frameOfPresentedViewInContainerView: CGRect {
        guard let containerView = containerView else {
            return CGRect.zero
        }
        let containerViewBounds = containerView.bounds
        let presentedViewContentSize = size(forChildContentContainer: self.presentedViewController, withParentContainerSize: containerViewBounds.size)

        var presentedViewControllerFrame = containerViewBounds
        presentedViewControllerFrame.size.height = presentedViewContentSize.height
        presentedViewControllerFrame.origin.y = containerViewBounds.maxY - presentedViewContentSize.height
        return presentedViewControllerFrame
    }

    override func containerViewWillLayoutSubviews() {
        super.containerViewWillLayoutSubviews()
        dimmingView?.frame = containerView?.bounds ?? CGRect.zero
        presentationWrappingView?.frame = frameOfPresentedViewInContainerView
    }

    // MARK: Tap Gesture Recognizer

    @objc func dimmingViewTapped(_: UITapGestureRecognizer) {
        dimmingViewTapAction?()
    }
}

extension YHBottomPresentationController: UIViewControllerAnimatedTransitioning {
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return (transitionContext?.isAnimated ?? false) ? 0.35 : 0
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        let fromViewController = transitionContext.viewController(forKey: .from)
        let toViewController = transitionContext.viewController(forKey: .to)
        let toView = transitionContext.view(forKey: .to)
        let fromView = transitionContext.view(forKey: .from)

        let containerView = transitionContext.containerView
        let isPresenting = (fromViewController === presentingViewController)
        var fromViewFinalFrame = CGRect.zero
        var toViewInitialFrame = CGRect.zero
        var toViewFinalFrame = CGRect.zero

        if let fromViewController = fromViewController {
            _ = transitionContext.initialFrame(for: fromViewController)
            fromViewFinalFrame = transitionContext.finalFrame(for: fromViewController)
        }

        if let toViewController = toViewController {
            toViewInitialFrame = transitionContext.initialFrame(for: toViewController)
            toViewFinalFrame = transitionContext.finalFrame(for: toViewController)
        }
        if let toView = toView {
            containerView.addSubview(toView)
        }

        if isPresenting {
            toViewInitialFrame.origin = CGPoint(x: containerView.bounds.minX, y: containerView.bounds.maxY)
            toViewInitialFrame.size = toViewFinalFrame.size
            if let toView = toView {
                toView.frame = toViewInitialFrame
            }
        } else {
            if let fromView = fromView {
                fromViewFinalFrame = fromView.frame.offsetBy(dx: 0, dy: fromView.frame.size.height)
            }
        }
        let transitionDuration = self.transitionDuration(using: transitionContext)
        UIView.animate(withDuration: transitionDuration) {
            if isPresenting {
                if let toView = toView {
                    toView.frame = toViewFinalFrame
                }
            } else {
                if let fromView = fromView {
                    fromView.frame = fromViewFinalFrame
                }
            }
        } completion: { _ in
            let wasCancelled = transitionContext.transitionWasCancelled
            transitionContext.completeTransition(!wasCancelled)
        }
    }
}

extension YHBottomPresentationController: UIViewControllerTransitioningDelegate {
    func presentationController(forPresented presented: UIViewController, presenting _: UIViewController?, source _: UIViewController) -> UIPresentationController? {
        assert(presentedViewController === presented, "You didn't initialize \(self) with the correct presentedViewController.  Expected \(presented), got \(presentedViewController).")
        return self
    }

    func animationController(forPresented _: UIViewController, presenting _: UIViewController, source _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return self
    }

    func animationController(forDismissed _: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return self
    }
}
