import UIKit
import WidgetKit

@MainActor
final class AppContext {
    let core: GotchaCore
    lazy var notifications = NotificationCoordinator(context: self)
    private let window: UIWindow
    private(set) var tabs = UITabBarController()
    private(set) var navigationControllers: [UINavigationController] = []

    init(window: UIWindow) {
        self.window = window
        let storageDirectory = FileManager.default.containerURL(
            forSecurityApplicationGroupIdentifier: "group.de.rfc1437.gotcha"
        )?.path
        core = GotchaCore(storageDirectory: storageDirectory)
        applyAppearance()
    }

    func makeRootController() -> UIViewController {
        let roots: [UIViewController] = [
            HomeViewController(context: self),
            repositoryRoot(mode: .issues),
            repositoryRoot(mode: .commits),
            PullsViewController(context: self),
            repositoryRoot(mode: .milestones),
        ]
        let items = [
            ("Home", "house", "house.fill"),
            ("Issues", "exclamationmark.circle", "exclamationmark.circle.fill"),
            ("Repos", "books.vertical", "books.vertical.fill"),
            ("PRs", "arrow.triangle.pull", "arrow.triangle.pull"),
            ("Milestones", "flag", "flag.fill"),
        ]
        navigationControllers = zip(roots, items).map { root, item in
            let navigation = UINavigationController(rootViewController: root)
            navigation.tabBarItem = UITabBarItem(
                title: item.0,
                image: UIImage(systemName: item.1),
                selectedImage: UIImage(systemName: item.2)
            )
            return navigation
        }
        tabs.viewControllers = navigationControllers
        return tabs
    }

    func showStartupErrorIfNeeded() {
        guard let message = core.startupError() else { return }
        tabs.present(errorAlert(message), animated: true)
    }

    func selectServer(index: UInt32) throws {
        try core.selectServer(index: index)
        reloadAfterServerChange()
    }

    func reloadAfterServerChange() {
        let replacements: [(Int, UIViewController)] = [
            (0, HomeViewController(context: self)),
            (1, repositoryRoot(mode: .issues)),
            (2, repositoryRoot(mode: .commits)),
            (3, PullsViewController(context: self)),
            (4, repositoryRoot(mode: .milestones)),
        ]
        for (index, root) in replacements {
            navigationControllers[index].setViewControllers([root], animated: false)
        }
        WidgetCenter.shared.reloadAllTimelines()
    }

    @discardableResult
    func route(widgetURL: URL) -> Bool {
        guard widgetURL.scheme == "gotcha" else { return false }
        let index: Int
        switch widgetURL.host {
        case "home": index = 0
        case "pulls": index = 3
        default: return false
        }
        tabs.selectedIndex = index
        navigationControllers[index].popToRootViewController(animated: false)
        return true
    }

    func didAddServer(index: UInt32) throws {
        try selectServer(index: index)
    }

    func applyAppearance() {
        switch core.settings().appearance {
        case 1: window.overrideUserInterfaceStyle = .light
        case 2: window.overrideUserInterfaceStyle = .dark
        default: window.overrideUserInterfaceStyle = .unspecified
        }
    }

    func route(_ activity: ActivityRow) {
        route(
            serverId: nil,
            target: activity.target,
            owner: activity.owner,
            repository: activity.repository,
            number: activity.number,
            sha: activity.sha
        )
    }

    func route(_ notification: NotificationRow) {
        route(
            serverId: notification.serverId,
            target: notification.target,
            owner: notification.owner,
            repository: notification.repository,
            number: notification.number,
            sha: notification.sha
        )
    }

    func route(notificationUserInfo userInfo: [AnyHashable: Any]) {
        let target: ActivityTargetKind
        switch userInfo["target"] as? String {
        case "repository": target = .repository
        case "issue": target = .issue
        case "pull": target = .pullRequest
        case "commit": target = .commit
        default: target = .none
        }
        route(
            serverId: userInfo["serverId"] as? String,
            target: target,
            owner: userInfo["owner"] as? String ?? "",
            repository: userInfo["repository"] as? String ?? "",
            number: (userInfo["number"] as? NSNumber)?.int64Value ?? 0,
            sha: userInfo["sha"] as? String ?? ""
        )
    }

    private func route(
        serverId: String?,
        target: ActivityTargetKind,
        owner: String,
        repository: String,
        number: Int64,
        sha: String
    ) {
        if let serverId {
            guard let index = core.servers().firstIndex(where: { $0.id == serverId }) else {
                tabs.present(errorAlert("That notification's server is no longer configured."), animated: true)
                return
            }
            if core.activeServerIndex() != UInt32(index) {
                do {
                    try selectServer(index: UInt32(index))
                } catch {
                    tabs.present(errorAlert(error.localizedDescription), animated: true)
                    return
                }
            }
        }
        let navigation = navigationControllers[0]
        tabs.selectedIndex = 0
        switch target {
        case .repository:
            navigation.pushViewController(
                IssuesViewController(
                    context: self,
                    owner: owner,
                    repository: repository
                ),
                animated: true
            )
        case .issue:
            navigation.pushViewController(
                IssueViewController(
                    context: self,
                    owner: owner,
                    repository: repository,
                    number: number
                ),
                animated: true
            )
        case .pullRequest:
            navigation.pushViewController(
                PullViewController(
                    context: self,
                    owner: owner,
                    repository: repository,
                    number: number
                ),
                animated: true
            )
        case .commit:
            navigation.pushViewController(
                FilesViewController(
                    context: self,
                    owner: owner,
                    repository: repository,
                    sha: sha
                ),
                animated: true
            )
        case .none:
            break
        }
    }

    func symbol(_ name: String) -> UIImage? {
        UIImage(systemName: name)
    }

    private func repositoryRoot(mode: RepositoryPane) -> UIViewController {
        if core.activeServerIndex() == nil {
            return ServersViewController(context: self)
        }
        return RepositoriesViewController(context: self, mode: mode)
    }
}
