//
//  SubNavManagerController.swift
//  MVMCoreUI
//
//  Created by Scott Pfeil on 10/22/21.
//  Copyright © 2021 Verizon Wireless. All rights reserved.
//

import Foundation
import MVMCore

open class SubNavManagerController: ViewController, MVMCoreViewManagerProtocol, TabsDelegate, MVMCorePresentationDelegateProtocol, SubNavSwipeNavigationProtocol {
    
    /// The current managed view controller
    private var viewController: UIViewController
    
    /// A list of cached controllers.
    private var viewControllers: [UIViewController?]

    private var tabsModel: TabsModel
    public lazy var tabs: Tabs = {
        let tabs = Tabs(model: tabsModel, delegateObjectIVar, nil)
        tabs.delegate = self
        return tabs
    }()
    
    public lazy var line: Line = {
        return Line(model: LineModel(type: .standard), delegateObjectIVar, nil)
    }()
    
    public lazy var subNavigationController: UINavigationController = {
        let subNavigationController = SubNavManagerNavigationController(rootViewController: viewController)
        subNavigationController.view.translatesAutoresizingMaskIntoConstraints = false
        return subNavigationController
    }()
        
    /// Interactor for swiping.
    public var customInteractor: SubNavInteractor?
    
    /// The index to go to.
    private var index: Int?
    
    /// Flag for if we need to call a track action.
    private var needToTrackTabSelect = false
    
    public init(viewController: UIViewController, tabsModel: TabsModel, loadObject: MVMCoreLoadObject, shouldEnableSwipeGestures: Bool) {
        self.viewController = viewController
        self.tabsModel = tabsModel
        viewControllers = [UIViewController?](repeating: nil, count: tabsModel.tabs.count)
        viewControllers[tabsModel.selectedIndex] = viewController
        super.init(nibName: nil, bundle: nil)
        setup(with: loadObject, shouldEnableSwipeGestures: shouldEnableSwipeGestures)
    }
    
    public init(viewControllers: [UIViewController], tabsModel: TabsModel, loadObject: MVMCoreLoadObject, shouldEnableSwipeGestures: Bool) {
        self.tabsModel = tabsModel
        self.viewControllers = viewControllers
        viewController = viewControllers[tabsModel.selectedIndex]
        super.init(nibName: nil, bundle: nil)
        setup(with: loadObject, shouldEnableSwipeGestures: shouldEnableSwipeGestures)
    }
    
    func setup(with loadObject: MVMCoreLoadObject, shouldEnableSwipeGestures: Bool) {
        self.loadObject = loadObject
        pageType = loadObject.pageType
        if shouldEnableSwipeGestures {
            customInteractor = SubNavInteractor(viewController: self)
        }
        if let controller = viewController as? (UIViewController & MVMCoreViewManagerViewControllerProtocol) {
            MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: controller)
        }
        hideNavigationBarLine(for: viewController)
    }
    
    required public init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    open override func loadView() {
        let view = View()
        view.translatesAutoresizingMaskIntoConstraints = true
        
        view.addSubview(tabs)
        view.addSubview(line)
        addChild(subNavigationController)
        view.addSubview(subNavigationController.view)
        subNavigationController.didMove(toParent: self)
        
        NSLayoutConstraint.activate([
            tabs.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            view.trailingAnchor.constraint(equalTo: tabs.trailingAnchor),
            line.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            view.trailingAnchor.constraint(equalTo: line.trailingAnchor),
            subNavigationController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            view.trailingAnchor.constraint(equalTo: subNavigationController.view.trailingAnchor),
            tabs.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
            line.topAnchor.constraint(equalTo: tabs.bottomAnchor),
            subNavigationController.view.topAnchor.constraint(equalTo: line.bottomAnchor),
            view.bottomAnchor.constraint(equalTo: subNavigationController.view.bottomAnchor)
        ])
        self.view = view
    }
    
    open override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        // Notify the view controller it is showing.
        if manager == nil {
            (viewController as? MVMCoreViewManagerViewControllerProtocol)?.viewControllerReady?(inManager: self)
        }
    }
    
    open override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        
        // Notify showing view we will disappear.
        (viewController as? MVMCoreViewManagerViewControllerProtocol)?.managerWillDisappear?(self)
    }
    
    /// Hides the navigation bar for the page.
    open func hideNavigationBarLine(for viewController: UIViewController) {
        (viewController as? PageProtocol)?.pageModel?.navigationBar?.line?.type = .none
    }
    
    open override func updateViews() {
        super.updateViews()
        if screenSizeChanged() {
            tabs.updateView(view.bounds.size.width)
        }
    }
    
    // MARK: - MVMCoreLoadDelegateProtocol
    
    open func shouldContinue(toErrorPage loadObject: MVMCoreLoadObject, error: MVMCoreErrorObject?) -> Bool {
        // Push error screens so they do not replace the tab page.
        loadObject.requestParameters?.navigationController = navigationController
        loadObject.requestParameters?.loadStyle = .push
        return true
    }
    
    // MARK: - Action Related
    
    /// Logs the action for the selected tab.
    open func trackSelectTab() {
        guard let action = tabs.tabsModel?.tabs[tabs.selectedIndex].action,
              let json = MVMCoreUIActionHandler.shared()?.convertActionToJSON(action, delegateObject: delegateObjectIVar) else { return }
        MVMCoreUIActionHandler.shared()?.logAction(json, additionalData: getAdditionalDataForNewTabLoad(indexPath: IndexPath(row: tabs.selectedIndex, section: 0)), delegateObject: delegateObjectIVar)
    }
    
    /// Allow override of additioonal data for tab press action
    open func getAdditionalDataForNewTabLoad(indexPath: IndexPath) -> [AnyHashable: Any]? {
        return ["tabBarPressed": true, KeySourceModel: tabsModel]
    }
    
    /// Allows modification of requestParameters object for openPage request
    open func getRequestParametersForNewTabLoad(requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) -> MVMCoreRequestParameters {
        requestParameters.navigationController = subNavigationController
        requestParameters.loadStyle = .replaceCurrent
        requestParameters.tabWasPressed = true
        return requestParameters
    }

    open override func handleOpenPage(for requestParameters: MVMCoreRequestParameters, actionInformation: [AnyHashable : Any]?, additionalData: [AnyHashable : Any]?) {
        var requestParameters = requestParameters
        guard let additionalData = additionalData,
           additionalData.boolForKey("tabBarPressed") else {
               // Pass to delegate
               if (viewController as? MVMCoreActionDelegateProtocol)?.handleOpenPage?(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData) == nil {
                   super.handleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData)
               }
               return
        }
        // Allow opportunity to modify parameters.
        requestParameters = getRequestParametersForNewTabLoad(requestParameters: requestParameters, actionInformation: actionInformation, additionalData: additionalData)
        super.handleOpenPage(for: requestParameters, actionInformation: actionInformation, additionalData: additionalData)
    }
    
    // MARK: - MVMCorePresentationDelegateProtocol
    
    public func navigationController(_ navigationController: UINavigationController, interactiveTransitionWasCanceled canceled: Bool) {
        needToTrackTabSelect = false
        index = nil
    }
    
    public func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        guard navigationController == subNavigationController,
              let index = index else { return nil }
        
        // Tab bars animate left and right navigation accordingly.
        if tabs.selectedIndex < index {
            return SubNavSwipeAnimator(swipingDirection: .left, controller: self)
        } else if tabs.selectedIndex > index {
            return SubNavSwipeAnimator(swipingDirection: .right, controller: self)
        } else {
            return nil
        }
    }
    
    public func navigationController(_ navigationController: UINavigationController, interactionControllerForAnimationController animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? {
        // Only percent interact if we've already loaded the view controller
        guard let customInteractor = customInteractor,
              let index = index,
              customInteractor.panning,
              viewControllers[index] != nil else { return nil }
        customInteractor.interactive = true
        return customInteractor
    }
    
    /// Handles when the controller has committed to be changed.
    private func commitTo(controller: UIViewController) {
        guard let index = index,
              index != tabs.selectedIndex else { return }
        viewController = controller
        pageType = (viewController as? MVMCoreViewControllerProtocol)?.pageType
        hideNavigationBarLine(for: controller)
        if let viewController = getCurrentViewController() {
            manager?.willDisplay?(viewController)
        }
        tabs.selectIndex(index, animated: true)
        self.index = nil
    }
    
    public func navigationController(_ navigationController: UINavigationController, willDisplay viewController: UIViewController) {
        guard navigationController == subNavigationController else { return }
        
        if let viewController = viewController as? UIViewController & MVMCoreViewManagerViewControllerProtocol & MVMCoreViewControllerProtocol {
            MVMCoreViewManagerViewControllerProtocolHelper.helpSetManager(self, viewController: viewController)

            // Cache the controller.
            if viewController.shouldCacheInManager?() ?? true,
               let index = index {
                viewControllers[index] = viewController
            }
        }
        
        // Wait to consider page loaded until after transition if we are custom animating swipe, otherwise do it now.
        guard customInteractor?.interactive != true else { return }
        commitTo(controller: viewController)
    }
    
    public func navigationController(_ navigationController: UINavigationController, displayedViewController viewController: UIViewController) {
        guard navigationController == subNavigationController else { return }
        // Need to track swipe action.
        if needToTrackTabSelect {
            needToTrackTabSelect = false
            trackSelectTab()
        }
        
        // Consider the page loaded if we have not already
        commitTo(controller: viewController)
        (viewController as? MVMCoreViewManagerViewControllerProtocol)?.viewControllerReady?(inManager: self)
        if let viewController = getCurrentViewController() {
            manager?.displayedViewController?(viewController)
        }
    }
    
    // MARK: - TabsDelegate
    
    // We will manually manage tab selection due to possible swipe interactions.
    open func shouldSelectItem(_ indexPath: IndexPath, tabs: Tabs) -> Bool {
        guard tabs.selectedIndex != indexPath.row else { return false }
        index = indexPath.row
        if let controller = viewControllers[indexPath.row] {
            // Load controller from the cache
            needToTrackTabSelect = true
            MVMCoreNavigationHandler.shared()?.replaceTopViewController(with: controller, navigationController: subNavigationController, animated: true, delegate: self, replaceInStack: false, completionHandler: nil)
        } else if let tabsModel = tabs.tabsModel,
                  let action = tabsModel.tabs[indexPath.row].action {
            // Perform the tab action
            MVMCoreActionHandler.shared()?.asyncHandleAction(with: action, additionalData: getAdditionalDataForNewTabLoad(indexPath: indexPath), delegateObject: delegateObjectIVar)
        }
        return false
    }
    
    // Not currently used.
    open func didSelectItem(_ indexPath: IndexPath, tabs: Tabs) {}
    
    // MARK: - MVMCoreViewManagerViewControllerProtocol
    
    open override func viewControllerReady(inManager manager: UIViewController & MVMCoreViewManagerProtocol) {
        // Pass on down
        (viewController as? MVMCoreViewManagerViewControllerProtocol)?.viewControllerReady?(inManager: self)
    }
    
    // MARK: - MVMCoreViewManagerProtocol
    
    open func getCurrentViewController() -> UIViewController? {
        MVMCoreUIUtility.getViewControllerTraversingManagers(viewController)
    }
    
    open func containsPage(withPageType pageType: String?) -> Bool {
        guard let pageType = pageType else { return false }
        return viewControllers.contains { controller in
           return (controller as? MVMCoreViewControllerProtocol)?.pageType == pageType
        }
    }
    
    open func newDataReceived(in viewController: UIViewController) {
        if viewController == self.viewController {
            hideNavigationBarLine(for: viewController)
        }
        manager?.newDataReceived?(in: viewController)
    }
    
    public func willDisplay(_ viewController: UIViewController) {
        hideNavigationBarLine(for: viewController)
        manager?.willDisplay?(viewController)
    }
    
    public func displayedViewController(_ viewController: UIViewController) {
        manager?.displayedViewController?(viewController)
    }
    
    // MARK: - MVMCoreUISwipeNavigationProtocol
    
    public func swipeLeft() {
        guard tabs.selectedIndex < (tabs.tabsModel?.tabs.count ?? 0) - 1 else { return }
        _ = shouldSelectItem(IndexPath(row: tabs.selectedIndex + 1, section: 0), tabs: tabs)
    }
    
    public func swipeRight() {
        guard tabs.selectedIndex != 0 else { return }
        _ = shouldSelectItem(IndexPath(row: tabs.selectedIndex - 1, section: 0), tabs: tabs)
    }
    
    public func update(percentage: CGFloat) {
        guard customInteractor?.interactive == true,
        let index = index else { return }
        tabs.progress(from: tabs.selectedIndex, toIndex: index, percentage: percentage)
    }
}
