// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.

import UIKit
import SessionUtilitiesKit

open class Modal: UIViewController, UIGestureRecognizerDelegate {
    private static let cornerRadius: CGFloat = 11
    
    public enum DismissType: Equatable, Hashable {
        case single
        case recursive
    }
    
    private let dismissType: DismissType
    private let afterClosed: (() -> ())?
    
    // MARK: - Components
    
    private lazy var dimmingView: UIView = {
        let result = UIVisualEffectView()
        
        ThemeManager.onThemeChange(observer: result) { [weak result] theme, _ in
            result?.effect = UIBlurEffect(
                style: (theme.interfaceStyle == .light ?
                    UIBlurEffect.Style.systemUltraThinMaterialLight :
                    UIBlurEffect.Style.systemUltraThinMaterial
                )
            )
        }
        
        return result
    }()
    
    lazy var containerView: UIView = {
        let result: UIView = UIView()
        result.clipsToBounds = false
        result.themeBackgroundColor = .alert_background
        result.themeShadowColor = .black
        result.layer.cornerRadius = Modal.cornerRadius
        result.layer.shadowRadius = 10
        result.layer.shadowOpacity = 0.4
        
        return result
    }()
    
    public lazy var contentView: UIView = {
        let result: UIView = UIView()
        result.clipsToBounds = true
        result.layer.cornerRadius = Modal.cornerRadius
        
        return result
    }()
    
    public lazy var cancelButton: UIButton = {
        let result: UIButton = Modal.createButton(title: "cancel".localized(), titleColor: .textPrimary)
        result.addTarget(self, action: #selector(cancel), for: .touchUpInside)
                
        return result
    }()
    
    // MARK: - Lifecycle
    
    public init(
        targetView: UIView? = nil,
        dismissType: DismissType = .recursive,
        afterClosed: (() -> ())? = nil
    ) {
        self.dismissType = dismissType
        self.afterClosed = afterClosed
        
        super.init(nibName: nil, bundle: nil)
        
        // Ensure the modal doesn't crash on iPad when being presented
        Modal.setupForIPadIfNeeded(self, targetView: (targetView ?? self.view))
    }
    
    required public init?(coder: NSCoder) {
        fatalError("Use init(targetView:afterClosed:) instead")
    }
    
    public override func viewDidLoad() {
        super.viewDidLoad()
        
        navigationItem.backButtonTitle = ""
        view.themeBackgroundColor = .clear
        ThemeManager.applyNavigationStylingIfNeeded(to: self)

        setNeedsStatusBarAppearanceUpdate()
        
        view.addSubview(dimmingView)
        view.addSubview(containerView)
        
        containerView.addSubview(contentView)
        
        dimmingView.pin(to: view)
        contentView.pin(to: containerView)
        
        if UIDevice.current.isIPad {
            containerView.set(.width, to: Values.iPadModalWidth)
            containerView.center(in: view)
        }
        else {
            containerView.leadingAnchor
                .constraint(equalTo: view.leadingAnchor, constant: Values.veryLargeSpacing)
                .isActive = true
            view.trailingAnchor
                .constraint(equalTo: containerView.trailingAnchor, constant: Values.veryLargeSpacing)
                .isActive = true
            containerView.center(.vertical, in: view)
        }
        
        // Gestures
        let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(close))
        swipeGestureRecognizer.direction = .down
        dimmingView.addGestureRecognizer(swipeGestureRecognizer)
        
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(close))
        tapGestureRecognizer.delegate = self
        dimmingView.addGestureRecognizer(tapGestureRecognizer)
        
        populateContentView()
    }
    
    /// To be overridden by subclasses.
    open func populateContentView() {
        preconditionFailure("populateContentView() is abstract and must be overridden.")
    }
    
    public static func createButton(title: String, titleColor: ThemeValue) -> UIButton {
        let result: UIButton = UIButton()
        result.titleLabel?.font = .boldSystemFont(ofSize: Values.mediumFontSize)
        result.setTitle(title, for: .normal)
        result.setThemeTitleColor(titleColor, for: .normal)
        result.setThemeBackgroundColor(.alert_buttonBackground, for: .normal)
        result.setThemeBackgroundColor(.highlighted(.alert_buttonBackground), for: .highlighted)
        result.set(.height, to: Values.alertButtonHeight)
                
        return result
    }
    
    // MARK: - Interaction
    
    @objc public func cancel() {
        close()
    }
    
    @objc public final func close() {
        // Recursively dismiss all modals (ie. find the first modal presented by a non-modal
        // and get that to dismiss it's presented view controller)
        var targetViewController: UIViewController? = self
        
        switch dismissType {
            case .single: break
            
            case .recursive:
                while targetViewController?.presentingViewController is Modal {
                    targetViewController = targetViewController?.presentingViewController
                }
        }
        
        targetViewController?.presentingViewController?.dismiss(animated: true) { [weak self] in
            self?.afterClosed?()
        }
    }
    
    // MARK: - UIGestureRecognizerDelegate
    
    public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool {
        let location: CGPoint = touch.location(in: contentView)
        
        return !contentView.point(inside: location, with: nil)
    }
}

// MARK: - Convenience

public extension Modal {
    static func setupForIPadIfNeeded(_ viewController: UIViewController, targetView: UIView) {
        if UIDevice.current.isIPad {
            viewController.popoverPresentationController?.permittedArrowDirections = []
            viewController.popoverPresentationController?.sourceView = targetView
            viewController.popoverPresentationController?.sourceRect = targetView.bounds
        }
    }
}