//
//  MainSplitViewController.swift
//  Tusker
//
//  Created by Shadowfacts on 6/23/20.
//  Copyright © 2020 Shadowfacts. All rights reserved.
//

import UIKit
import Combine

class MainSplitViewController: UISplitViewController {

    weak var mastodonController: MastodonController!
    
    private var sidebar: MainSidebarViewController!
    private var fastAccountSwitcher: FastAccountSwitcherViewController?
    
    // Keep track of navigation stacks per-item so that we can only ever use a single navigation controller
    private var navigationStacks: [MainSidebarViewController.Item: [UIViewController]] = [:]
    
    private var tabBarViewController: MainTabBarViewController!
    
    private var navigationMode: Preferences.WidescreenNavigationMode!
    private var secondaryNavController: NavigationControllerProtocol! {
        viewController(for: .secondary) as? NavigationControllerProtocol
    }
    
    private var cancellables = Set<AnyCancellable>()
    
    private var sidebarVisibile: Bool {
        get {
            (UserDefaults.standard.object(forKey: "MainSplitViewControllerSidebarVisible") as? Bool) ?? true
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "MainSplitViewControllerSidebarVisible")
        }
    }
    
    init(mastodonController: MastodonController) {
        self.mastodonController = mastodonController
        
        super.init(style: .doubleColumn)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        preferredDisplayMode = .oneBesideSecondary
        preferredSplitBehavior = .tile
        delegate = self
        
        sidebar = MainSidebarViewController(mastodonController: mastodonController)
        sidebar.sidebarDelegate = self
        setViewController(sidebar, for: .primary)
        primaryBackgroundStyle = .sidebar
        if sidebarVisibile {
            show(.primary)
        } else {
            hide(.primary)
        }
        
        let nav: UIViewController
        navigationMode = Preferences.shared.widescreenNavigationMode
        switch navigationMode! {
        case .stack:
            nav = EnhancedNavigationViewController()
        case .splitScreen:
            nav = SplitNavigationController()
        case .multiColumn:
            nav = MultiColumnNavigationController()
        }
        setViewController(nav, for: .secondary)
        
        // don't unnecesarily construct a content VC unless the we're in actually split mode
        // when we change from compact -> split for the first time, the VC will be transferred anyways
        if traitCollection.horizontalSizeClass != .compact {
            select(item: .tab(.timelines))
        }
        
        if UIDevice.current.userInterfaceIdiom != .mac {
            let switcher = FastAccountSwitcherViewController()
            fastAccountSwitcher = switcher
            switcher.itemOrientation = .iconsLeading
            switcher.view.translatesAutoresizingMaskIntoConstraints = false
            switcher.delegate = self
            // accessing .view unconditionally loads the view, which we don't want to happen
            // because the sidebar view not being loaded is how we know not to transfer nav state
            // in splitViewControllerDidCollapse on devices where the sidebar is never shown
            sidebar.onViewDidLoad = { [unowned self] in
                self.sidebar.view.addGestureRecognizer(switcher.createSwitcherGesture())
                let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(sidebarTapped))
                tapRecognizer.cancelsTouchesInView = false
                self.sidebar.view.addGestureRecognizer(tapRecognizer)
            }
        }
        
        tabBarViewController = MainTabBarViewController(mastodonController: mastodonController)
        setViewController(tabBarViewController, for: .compact)
        
        addKeyCommand(MenuController.composeCommand)
        
        MenuController.sidebarItemKeyCommands.forEach(addKeyCommand(_:))
        
        Preferences.shared.$widescreenNavigationMode
            .sink { [unowned self] in
                self.updateNavigationMode($0)
            }
            .store(in: &cancellables)
    }
    
    private func updateNavigationMode(_ mode: Preferences.WidescreenNavigationMode) {
        guard mode != navigationMode else {
            return
        }
        navigationMode = mode
        
        let viewControllers = secondaryNavController.viewControllers
        secondaryNavController.viewControllers = []
        // Setting viewControllers = [] doesn't remove the VC views from their superviews immediately,
        // so do that ourselves so we can re-parent the VCs to the new nav controller.
        for viewController in viewControllers {
            viewController.viewIfLoaded?.removeFromSuperview()
        }
        
        let newNav: NavigationControllerProtocol
        switch mode {
        case .stack:
            newNav = EnhancedNavigationViewController()
        case .splitScreen:
            newNav = SplitNavigationController()
        case .multiColumn:
            newNav = MultiColumnNavigationController()
        }
        newNav.viewControllers = viewControllers
        self.setViewController(newNav, for: .secondary)
    }
    
    func select(item: MainSidebarViewController.Item) {
        secondaryNavController.viewControllers = getOrCreateNavigationStack(item: item)
    }
    
    func navigationStackFor(item: MainSidebarViewController.Item) -> [UIViewController]? {
        if sidebar.selectedItem == item {
            return secondaryNavController.viewControllers
        } else {
            return navigationStacks[item]
        }
    }
    
    func getOrCreateNavigationStack(item: MainSidebarViewController.Item) -> [UIViewController] {
        if let existing = navigationStacks[item], existing.count > 0 {
            return existing
        } else {
            let new = [item.createRootViewController(mastodonController)!]
            navigationStacks[item] = new
            return new
        }
    }
    
    override func show(_ vc: UIViewController, sender: Any?) {
        if traitCollection.horizontalSizeClass == .regular {
            secondaryNavController.show(vc, sender: sender)
        } else {
            super.show(vc, sender: sender)
        }
    }
    
    @objc func handleSidebarCommandTimelines() {
        sidebar.select(item: .tab(.timelines), animated: false)
        select(item: .tab(.timelines))
    }
    
    @objc func handleSidebarCommandNotifications() {
        sidebar.select(item: .tab(.notifications), animated: false)
        select(item: .tab(.notifications))
    }
    
    @objc func handleSidebarCommandExplore() {
        sidebar.select(item: .tab(.explore), animated: false)
        select(item: .tab(.explore))
    }
    
    @objc func handleSidebarCommandBookmarks() {
        sidebar.select(item: .bookmarks, animated: false)
        select(item: .bookmarks)
    }
    
    @objc func handleSidebarCommandMyProfile() {
        sidebar.select(item: .tab(.myProfile), animated: false)
        select(item: .tab(.myProfile))
    }
    
    @objc private func sidebarTapped() {
        fastAccountSwitcher?.hide()
    }
    
    @objc func handleComposeKeyCommand() {
        compose(editing: nil)
    }

}

extension MainSplitViewController: UISplitViewControllerDelegate {
    /// Transfer the navigation stack for a sidebar item to a destination navgiation controller.
    /// - Parameter dropFirst: Remove the first view controller from the item's navigation stack before transferring.
    /// - Parameter append: Append the item's navigation stack to the destination nav controller's instead of replacing it.
    private func transferNavigationStack(from item: MainSidebarViewController.Item, to destination: UINavigationController, dropFirst: Bool = false, append: Bool = false) {
        var itemNavStack: [UIViewController]
        if item == sidebar.selectedItem {
            itemNavStack = secondaryNavController.viewControllers
            secondaryNavController.viewControllers = []
            // Sometimes removing a VC from the viewControllers array doesn't immediately remove it's view from the hierarchy
            for vc in itemNavStack {
                vc.viewIfLoaded?.removeFromSuperview()
            }
        } else {
            itemNavStack = navigationStacks[item] ?? []
            navigationStacks.removeValue(forKey: item)
        }
        if itemNavStack.isEmpty {
            itemNavStack = [item.createRootViewController(mastodonController)!]
        }
        
        if dropFirst {
            itemNavStack.remove(at: 0)
        }
        
        if append {
            destination.viewControllers += itemNavStack
        } else {
            destination.viewControllers = itemNavStack
        }
    }
    
    func splitViewControllerDidCollapse(_ svc: UISplitViewController) {
        // on iPhones, the sidebar VC is never loaded, but since this method is still called, we can't do anything
        guard sidebar.isViewLoaded else { return }
        
        // Transfer the nav stacks for all the sidebar items that map 1 <-> 1 with tabs
        for tab in [MainTabBarViewController.Tab.timelines, .notifications, .myProfile] {
            let tabNav = tabBarViewController.viewController(for: tab) as! UINavigationController
            if tabNav.isViewLoaded {
                transferNavigationStack(from: .tab(tab), to: tabNav)
            }
        }
        
        // Since several sidebar items map to the single Explore tab, we only transfer the
        // navigation stack of the most-recently used one.
        let mostRecentExploreItem: (MainSidebarViewController.Item, Date)? =
            sidebar.exploreTabItems.compactMap {
                if let timestamp = sidebar.itemLastSelectedTimestamps[$0] {
                    return ($0, timestamp)
                } else {
                    return nil
                }
            }.min {
                $0.1 > $1.1
            }
        if let mostRecentExploreItem = mostRecentExploreItem?.0,
           mostRecentExploreItem != .explore {
            let exploreNav = tabBarViewController.viewController(for: .explore) as! UINavigationController
            // Pop back to root, so we're appending to the Explore VC instead of some other VC
            exploreNav.popToRootViewController(animated: false)
            // Append so we don't replace the Explore VC
            transferNavigationStack(from: mostRecentExploreItem, to: exploreNav, append: true)
        }
        
        // Switch the tab bar to focus the same item as the sidebar has selected
        switch sidebar.selectedItem {
        case nil:
            break
            
        case let .tab(tab):
            // sidebar items that map 1 <-> 1 can be transferred directly
            tabBarViewController.select(tab: tab, dismissPresented: false)
            
        case .explore:
            // Search sidebar item maps to the Explore tab with the search controller/results visible
            // The nav stack can't be copied directly, since the split VC uses a different SearchViewController
            // so that explore items aren't shown multiple times.
            
            let exploreNav = tabBarViewController.viewController(for: .explore) as! UINavigationController
            // make sure there's a root ExploreViewController
            let explore: ExploreViewController
            if let existing = exploreNav.viewControllers.first as? ExploreViewController {
                explore = existing
                exploreNav.popToRootViewController(animated: false)
            } else {
                // If the Explore tab hasn't been loaded before, it's root view controller won't be loaded yet, so create and add it manually.
                explore = ExploreViewController(mastodonController: mastodonController)
                exploreNav.viewControllers = [explore]
            }
            // Make sure viewDidLoad is called so that the searchController/resultsController have been initialized
            explore.loadViewIfNeeded()
            
            let search = secondaryNavController.viewControllers.first as! InlineTrendsViewController
            if search.searchController?.isActive == true {
                // Copy the search query from the search VC to the Explore VC's search controller.
                let query = search.searchController.searchBar.text ?? ""
                explore.searchController.searchBar.text = query
                // Instruct the explore controller to show its search controller immediately upon its first appearance.
                // explore.searchController.isActive can't be set directly, see FB7814561
                explore.searchControllerStatusOnAppearance = !query.isEmpty
                // Copy the results from the search VC's results controller to avoid the delay introduced by an extra network request
                explore.resultsController.loadResults(from: search.resultsController)
            } else {
                // if there is more than just the InlineTrendsVC, and the search VC is not active,
                // then the user selected something from the trends screen
                if secondaryNavController.viewControllers.count >= 2 {
                    // make sure there's a corresponding trends VC in the collapsed nav that they can go back to
                    exploreNav.pushViewController(TrendsViewController(mastodonController: mastodonController), animated: false)
                }
            }
            
            // Transfer the navigation stack, dropping the search VC, to keep anything the user has opened
            transferNavigationStack(from: .explore, to: exploreNav, dropFirst: true, append: true)
            
            tabBarViewController.select(tab: .explore, dismissPresented: false)
            
        case .bookmarks, .favorites, .list(_), .savedHashtag(_), .savedInstance(_):
            tabBarViewController.select(tab: .explore, dismissPresented: false)
            // Make sure the Explore VC doesn't show its search bar when it appears, in case the user was previously
            // in compact mode and performing a search.
            let exploreNav = tabBarViewController.viewController(for: .explore) as! UINavigationController
            let explore = exploreNav.viewControllers.first as! ExploreViewController
            explore.searchControllerStatusOnAppearance = false
            
        case .listsHeader, .addList, .savedHashtagsHeader, .addSavedHashtag, .savedInstancesHeader, .addSavedInstance:
            // These items are not selectable in the sidebar collection view, so this code is unreachable.
            fatalError("unexpected selected sidebar item: \(sidebar.selectedItem!)")
        }
    }
    
    /// Transfer a navigation stack from a navigation controller belonging to the tab bar VC to a sidebar item.
    /// - Parameter skipFirst:The number of view controllers that should be skipped from the source navigation controller.
    /// - Parameter prepend: An optional view controller to prepend to the beginning of the navigation stack being moved.
    private func transferNavigationStack(from navController: UINavigationController, to item: MainSidebarViewController.Item, skipFirst: Int = 0, prepend: UIViewController? = nil) {
        let viewControllersToMove = navController.viewControllers.dropFirst(skipFirst)
        navController.viewControllers.removeLast(navController.viewControllers.count - skipFirst)
        
        // Sometimes removing a VC from the viewControllers array doesn't immediately remove it's view from the hierarchy
        for vc in viewControllersToMove {
            vc.viewIfLoaded?.removeFromSuperview()
        }
        
        if let prepend = prepend {
            navigationStacks[item] = [prepend] + viewControllersToMove
        } else {
            navigationStacks[item] = Array(viewControllersToMove)
        }
    }
    
    func splitViewControllerDidExpand(_ svc: UISplitViewController) {
        // For each sidebar item, transfer the existing navigation stasck from the tab bar controller to ourself.
        var exploreItem: MainSidebarViewController.Item?
        for tab in MainTabBarViewController.Tab.allCases {
            guard let tabNavController = tabBarViewController.viewController(for: tab) as? UINavigationController,
                  tabNavController.isViewLoaded else { continue }
            let tabNavigationStack = tabNavController.viewControllers
            
            switch tab {
            case .timelines, .notifications, .myProfile:
                // Items that map 1 <-> 1 to tabs can be transferred directly.
                let item = MainSidebarViewController.Item.tab(tab)
                transferNavigationStack(from: tabNavController, to: item)
                
            case .explore:
                // The Explore tab is more complicated since it encapsulates a bunch of screens which have top-level sidebar items.
                
                var skipFirst = 1
                var toPrepend: UIViewController? = nil
                
                // If the tab navigation stack has only one item or the search controller is active, it corresponds to the Search item
                // For other items, the 2nd VC in the nav stack determines which sidebar item they map to.
                // Search screen has special considerations, all others can be transferred directly.
                if tabNavigationStack.count == 1 || ((tabNavigationStack.first as? ExploreViewController)?.searchController?.isActive ?? false) {
                    exploreItem = .explore
                    // reuse the existing VC, if there is one
                    let searchVC = getOrCreateNavigationStack(item: .explore).first! as! InlineTrendsViewController
                    // load the view so that the search controller is accessible
                    searchVC.loadViewIfNeeded()
                    let explore = tabNavigationStack.first as! ExploreViewController
                    if let exploreSearchControler = explore.searchController,
                       let query = exploreSearchControler.searchBar.text {
                        // Transfer query to search VC
                        searchVC.searchController.searchBar.text = query
                        // If there is a query, make the search VC activate itself upon appearing
                        searchVC.searchControllerStatusOnAppearance = !query.isEmpty
                        // Transfer the results from the explore VC, to avoid an extra network request
                        searchVC.resultsController.loadResults(from: explore.resultsController)
                    }
                    // Insert the new search VC at the beginning of the new search nav stack
                    toPrepend = searchVC
                } else {
                    switch tabNavigationStack[1] {
                    case is BookmarksViewController:
                        exploreItem = .bookmarks
                    case is FavoritesViewController:
                        exploreItem = .favorites
                    case let listVC as ListTimelineViewController:
                        exploreItem = .list(listVC.list)
                    case let hashtagVC as HashtagTimelineViewController where sidebar.hasItem(.savedHashtag(hashtagVC.hashtagName)):
                        exploreItem = .savedHashtag(hashtagVC.hashtagName)
                    case let instanceVC as InstanceTimelineViewController:
                        exploreItem = .savedInstance(instanceVC.instanceURL)
                    case is TrendsViewController:
                        exploreItem = .explore
                        // skip transferring the ExploreViewController and TrendsViewController
                        skipFirst = 2
                        // prepend the InlineTrendsViewController
                        toPrepend = getOrCreateNavigationStack(item: .explore).first!
                    default:
                        // transfer the navigation stack prepending, the existing explore VC
                        // if there was other stuff on the explore stack, it will get discarded
                        toPrepend = getOrCreateNavigationStack(item: .explore).first!
                        exploreItem = .explore
                    }
                }
                transferNavigationStack(from: tabNavController, to: exploreItem!, skipFirst: skipFirst, prepend: toPrepend)
                
            case .compose:
                // The compose tab can't be activated, this is unreachable.
                fatalError("unreachable")
            }
        }
        
        // Transfer the selected tab from the tab bar VC to the sidebar
        switch tabBarViewController.selectedTab {
        case .timelines, .notifications, .myProfile:
            // These tabs map 1 <-> 1 with sidebar items
            let item = MainSidebarViewController.Item.tab(tabBarViewController.selectedTab)
            sidebar.select(item: item, animated: false)
            select(item: item)
            
        case .explore:
            // If the explore tab is active, the sidebar item is determined above when transferring the explore VC's nav stack
            sidebar.select(item: exploreItem!, animated: false)
            select(item: exploreItem!)
            
        default:
            return
        }
    }
    
    func splitViewController(_ svc: UISplitViewController, willHide column: UISplitViewController.Column) {
        if column == .primary {
            sidebarVisibile = false
        }
    }
    
    func splitViewController(_ svc: UISplitViewController, willShow column: UISplitViewController.Column) {
        if column == .primary {
            sidebarVisibile = true
        }
    }
}

extension MainSplitViewController: MainSidebarViewControllerDelegate {
    func sidebarRequestPresentCompose(_ sidebarViewController: MainSidebarViewController) {
        compose(editing: nil)
    }
    
    func sidebar(_ sidebarViewController: MainSidebarViewController, didSelectItem item: MainSidebarViewController.Item) {
        if let previous = sidebar.previouslySelectedItem {
            navigationStacks[previous] = secondaryNavController.viewControllers
        }
        select(item: item)
    }
    
    func sidebar(_ sidebarViewController: MainSidebarViewController, showViewController viewController: UIViewController) {
        if let previous = sidebar.previouslySelectedItem {
            navigationStacks[previous] = secondaryNavController.viewControllers
        }
        secondaryNavController.viewControllers = [viewController]
    }
}

fileprivate extension MainSidebarViewController.Item {
    func createRootViewController(_ mastodonController: MastodonController) -> UIViewController? {
        switch self {
        case let .tab(tab):
            return tab.createViewController(mastodonController)
        case .explore:
            return InlineTrendsViewController(mastodonController: mastodonController)
        case .bookmarks:
            return BookmarksViewController(mastodonController: mastodonController)
        case .favorites:
            return FavoritesViewController(mastodonController: mastodonController)
        case let .list(list):
            return ListTimelineViewController(for: list, mastodonController: mastodonController)
        case let .savedHashtag(name):
            return HashtagTimelineViewController(forNamed: name, mastodonController: mastodonController)
        case let .savedInstance(url):
            return InstanceTimelineViewController(for: url, parentMastodonController: mastodonController)
        case .listsHeader, .addList, .savedHashtagsHeader, .addSavedHashtag, .savedInstancesHeader, .addSavedInstance:
            return nil
        }
    }
}

extension MainSplitViewController: TuskerNavigationDelegate {
    var apiController: MastodonController! { mastodonController }
}

extension MainSplitViewController: StateRestorableViewController {
    func stateRestorationActivity() -> NSUserActivity? {
        if traitCollection.horizontalSizeClass == .compact {
            return tabBarViewController.stateRestorationActivity()
        } else if let currentItem = sidebar.selectedItem,
                  let navStack = navigationStackFor(item: currentItem),
                  let top = navStack.last as? StateRestorableViewController {
            return top.stateRestorationActivity()
        } else {
            stateRestorationLogger.fault("MainSplitViewController: Unable to create state restoration activity")
            return nil
        }
    }
}

extension MainSplitViewController: TuskerRootViewController {
    func select(route: TuskerRoute, animated: Bool) {
        guard traitCollection.horizontalSizeClass != .compact else {
            tabBarViewController?.select(route: route, animated: animated)
            return
        }
        guard presentedViewController == nil else {
            dismiss(animated: animated) {
                self.select(route: route, animated: animated)
            }
            return
        }
        let item: MainSidebarViewController.Item
        switch route {
        case .timelines:
            item = .tab(.timelines)
        case .notifications:
            item = .tab(.notifications)
        case .myProfile:
            item = .tab(.myProfile)
        case .explore:
            item = .explore
        case .bookmarks:
            item = .bookmarks
        case .list(id: let id):
            if let list = mastodonController.getCachedList(id: id) {
                item = .list(list)
            } else {
                return
            }
        }
        sidebar.select(item: item, animated: false)
        select(item: item)
    }
    
    func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? {
        if traitCollection.horizontalSizeClass == .compact {
            return tabBarViewController?.getTabController(tab: tab)
        } else {
            if tab == .compose {
                return nil
            } else if case .tab(tab) = sidebar.selectedItem {
                return secondaryNavController
            } else {
                return nil
            }
        }
    }
    
    func getNavigationDelegate() -> TuskerNavigationDelegate? {
        if traitCollection.horizontalSizeClass == .compact {
            return tabBarViewController.getNavigationDelegate()
        } else {
            return self
        }
    }
    
    func getNavigationController() -> NavigationControllerProtocol {
        if traitCollection.horizontalSizeClass == .compact {
            return tabBarViewController.getNavigationController()
        } else {
            return secondaryNavController
        }
    }
    
    func performSearch(query: String) {
        guard traitCollection.horizontalSizeClass != .compact else {
            // ensure the tab bar VC is loaded
            loadViewIfNeeded()
            tabBarViewController.performSearch(query: query)
            return
        }
        
        if sidebar.selectedItem != .explore {
            select(item: .explore)
        }
        
        guard let searchViewController = secondaryNavController.viewControllers.first as? InlineTrendsViewController else {
            return
        }
        
        secondaryNavController.popToRootViewController(animated: false)
        
        if searchViewController.isViewLoaded {
            DispatchQueue.main.async {
                searchViewController.searchController.isActive = true
            }
        } else {
            searchViewController.searchControllerStatusOnAppearance = true
            searchViewController.loadViewIfNeeded()
        }
        
        searchViewController.searchController.searchBar.text = query
        searchViewController.resultsController.performSearch(query: query)
    }
    
    func presentPreferences(completion: (() -> Void)?) {
        present(PreferencesNavigationController(mastodonController: mastodonController), animated: true, completion: completion)
    }
    
    func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
        guard presentedViewController == nil else {
            return .continue
        }
        if traitCollection.horizontalSizeClass == .compact {
            return tabBarViewController.handleStatusBarTapped(xPosition: xPosition)
        } else {
            let pointInSecondary = secondaryNavController.view.convert(CGPoint(x: xPosition, y: 0), from: view)
            if secondaryNavController.view.bounds.contains(pointInSecondary),
               let statusBarTappable = secondaryNavController as? StatusBarTappableViewController {
                return statusBarTappable.handleStatusBarTapped(xPosition: pointInSecondary.x)
            } else {
                return .continue
            }
        }
    }
}

extension MainSplitViewController: BackgroundableViewController {
    func sceneDidEnterBackground() {
        if traitCollection.horizontalSizeClass == .compact {
            tabBarViewController.sceneDidEnterBackground()
        } else {
            // todo: should this do the same for the sidebar VC as well?
            if let contentVC = viewController(for: .secondary) as? BackgroundableViewController {
                contentVC.sceneDidEnterBackground()
            }
        }
    }
}

extension MainSplitViewController: FastAccountSwitcherViewControllerDelegate {
    func fastAccountSwitcherAddToViewHierarchy(_ fastAccountSwitcher: FastAccountSwitcherViewController) {
        view.addSubview(fastAccountSwitcher.view)
        let currentAccount = fastAccountSwitcher.accountViews.first(where: \.isCurrent)!
        let myProfileCell = sidebar.myProfileCell()!
        NSLayoutConstraint.activate([
            currentAccount.centerYAnchor.constraint(equalTo: myProfileCell.centerYAnchor),
            
            fastAccountSwitcher.view.leadingAnchor.constraint(equalTo: sidebar.view.trailingAnchor),
            fastAccountSwitcher.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            fastAccountSwitcher.view.topAnchor.constraint(equalTo: view.topAnchor),
            fastAccountSwitcher.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])
    }
    func fastAccountSwitcher(_ fastAccountSwitcher: FastAccountSwitcherViewController, triggerZoneContains point: CGPoint) -> Bool {
        guard !isCollapsed,
              let cell = sidebar.myProfileCell() else {
            return false
        }
        let cellRect = cell.convert(cell.bounds, to: sidebar.view)
        return cellRect.contains(point)
    }
}

extension MainSplitViewController: AccountSwitchableViewController {
    var isFastAccountSwitcherActive: Bool {
        if isCollapsed {
            return tabBarViewController.isFastAccountSwitcherActive
        } else if let fastAccountSwitcher {
            return !fastAccountSwitcher.view.isHidden
        } else {
            return false
        }
    }
}
