//
//  AccountSwitchingContainerViewController.swift
//  Tusker
//
//  Created by Shadowfacts on 11/11/20.
//  Copyright © 2020 Shadowfacts. All rights reserved.
//

import UIKit
import ScreenCorners
import UserAccounts
import ComposeUI

protocol AccountSwitchableViewController: TuskerRootViewController {
    var isFastAccountSwitcherActive: Bool { get }
}

class AccountSwitchingContainerViewController: UIViewController {

    private var currentAccountID: String
    private(set) var root: AccountSwitchableViewController
    
    private var userActivities: [String: NSUserActivity] = [:]
    
    init(root: AccountSwitchableViewController, for account: UserAccountInfo) {
        self.currentAccountID = account.id
        self.root = root
        
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        embedChild(root)
    }
    
    func setRoot(_ newRoot: AccountSwitchableViewController, for account: UserAccountInfo, animating direction: AnimationDirection) {
        let oldRoot = self.root
        if direction == .none {
            oldRoot.removeViewAndController()
        }
        if let activity = oldRoot.stateRestorationActivity() {
            stateRestorationLogger.debug("AccountSwitchingContainer: saving \(activity.activityType, privacy: .public) for \(self.currentAccountID, privacy: .public)")
            userActivities[currentAccountID] = activity
        }
        
        self.currentAccountID = account.id
        self.root = newRoot
        embedChild(newRoot)
        
        if let activity = userActivities.removeValue(forKey: account.id) {
            stateRestorationLogger.debug("AccountSwitchingContainer: restoring \(activity.activityType, privacy: .public) for \(account.id, privacy: .public)")
            let context = StateRestorationUserActivityHandlingContext(root: newRoot)
            _ = activity.handleResume(manager: UserActivityManager(scene: view.window!.windowScene!, context: context))
            context.finalize(activity: activity)
        }
        
        if direction != .none {
            if UIAccessibility.prefersCrossFadeTransitions {
                newRoot.view.alpha = 0
                
                UIView.animate(withDuration: 0.4, delay: 0, options: .curveEaseInOut) {
                    newRoot.view.alpha = 1
                    oldRoot.view.alpha = 0
                } completion: { (_) in
                    oldRoot.removeViewAndController()
                }
            } else {
                let sign: CGFloat = direction == .downwards ? -1 : 1
                let newInitialOffset = sign * view.bounds.height
                let scale: CGFloat = 0.75
                
                newRoot.view.transform = CGAffineTransform(translationX: 0, y: newInitialOffset).scaledBy(x: 0.9, y: 0.9)
                newRoot.view.layer.masksToBounds = true
                newRoot.view.layer.cornerCurve = .continuous
                newRoot.view.layer.cornerRadius = view.window?.screen.displayCornerRadius ?? 0
                
                oldRoot.view.layer.masksToBounds = true
                oldRoot.view.layer.cornerCurve = .continuous
                oldRoot.view.layer.cornerRadius = view.window?.screen.displayCornerRadius ?? 0

                // only one edge is affected in each direction, i have no idea why
                if direction == .upwards {
                    oldRoot.additionalSafeAreaInsets.bottom = view.safeAreaInsets.bottom
                } else {
                    oldRoot.additionalSafeAreaInsets.top = view.safeAreaInsets.top
                }
                
                UIView.animate(withDuration: 0.4, delay: 0, options: .curveEaseInOut) {
                    oldRoot.view.transform = CGAffineTransform(translationX: 0, y: -newInitialOffset).scaledBy(x: scale, y: scale)
                    newRoot.view.transform = .identity
                } completion: { (_) in
                    oldRoot.removeViewAndController()
                    newRoot.view.layer.masksToBounds = false
                }
            }
        }
    }
}

extension AccountSwitchingContainerViewController {
    enum AnimationDirection {
        case none, downwards, upwards
    }
}

extension AccountSwitchingContainerViewController: TuskerRootViewController {
    func stateRestorationActivity() -> NSUserActivity? {
        loadViewIfNeeded()
        return root.stateRestorationActivity()
    }
    
    func compose(editing draft: Draft?, animated: Bool, isDucked: Bool) {
        loadViewIfNeeded()
        root.compose(editing: draft, animated: animated, isDucked: isDucked)
    }
    
    func select(route: TuskerRoute, animated: Bool) {
        loadViewIfNeeded()
        root.select(route: route, animated: animated)
    }
    
    func getTabController(tab: MainTabBarViewController.Tab) -> UIViewController? {
        loadViewIfNeeded()
        return root.getTabController(tab: tab)
    }
    
    func getNavigationDelegate() -> TuskerNavigationDelegate? {
        loadViewIfNeeded()
        return root.getNavigationDelegate()
    }
    
    func getNavigationController() -> NavigationControllerProtocol {
        loadViewIfNeeded()
        return root.getNavigationController()
    }
    
    func performSearch(query: String) {
        loadViewIfNeeded()
        root.performSearch(query: query)
    }
    
    func presentPreferences(completion: (() -> Void)?) {
        loadViewIfNeeded()
        root.presentPreferences(completion: completion)
    }
    
    func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
        loadViewIfNeeded()
        if root.isFastAccountSwitcherActive {
            return .stop
        } else {
            return root.handleStatusBarTapped(xPosition: xPosition)
        }
    }
}

extension AccountSwitchingContainerViewController: BackgroundableViewController {
    func sceneDidEnterBackground() {
        if let backgroundable = root as? BackgroundableViewController {
            backgroundable.sceneDidEnterBackground()
        }
    }
}
