//
//  TuskerNavigationDelegate.swift
//  Tusker
//
//  Created by Shadowfacts on 9/30/18.
//  Copyright © 2018 Shadowfacts. All rights reserved.
//

import UIKit
import SafariServices
import Pachyderm
import ComposeUI

@MainActor
protocol TuskerNavigationDelegate: UIViewController, ToastableViewController {
    nonisolated var apiController: MastodonController! { get }
}

extension TuskerNavigationDelegate {
    
    func show(_ vc: UIViewController) {
        if vc is LargeImageViewController || vc is GalleryViewController || vc is SFSafariViewController {
            present(vc, animated: true)
        } else {
            show(vc, sender: self)
        }
    }
    
    func selected(account accountID: String) {
        // don't open if the account is the same as the current one
        if let profileController = self as? ProfileStatusesViewController,
            profileController.accountID == accountID {
            return
        }
        
        show(ProfileViewController(accountID: accountID, mastodonController: apiController), sender: self)
    }
    
    func selected(mention: Mention) {
        show(ProfileViewController(accountID: mention.id, mastodonController: apiController), sender: self)
    }
    
    func selected(tag: Hashtag) {
        show(HashtagTimelineViewController(for: tag, mastodonController: apiController), sender: self)
    }
    
    func selected(url: URL, allowResolveStatuses: Bool = true, allowUniversalLinks: Bool = true) {
        func openSafari() {
            if Preferences.shared.useInAppSafari,
               url.scheme == "https" || url.scheme == "http" {
                let config = SFSafariViewController.Configuration()
                config.entersReaderIfAvailable = Preferences.shared.inAppSafariAutomaticReaderMode
                let vc = SFSafariViewController(url: url, configuration: config)
                vc.preferredControlTintColor = Preferences.shared.accentColor.color
                present(vc, animated: true)
            } else if UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url, options: [:])
            } else {
                var message = "The URL could not be opened."
                if let scheme = url.scheme {
                    message += " This can happen if you do not have an app installed for '\(scheme)://' URLs."
                }
                let alert = UIAlertController(title: "Invalid URL", message: message, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
                present(alert, animated: true)
            }
        }
        
        if allowResolveStatuses,
           isLikelyResolvableAsStatus(url) {
            show(ConversationViewController(resolving: url, mastodonController: apiController))
        } else if allowUniversalLinks,
                  Preferences.shared.openLinksInApps,
                  url.scheme == "https" || url.scheme == "http" {
            UIApplication.shared.open(url, options: [.universalLinksOnly: true]) { (success) in
                if (!success) {
                    openSafari()
                }
            }
        } else {
            openSafari()
        }
    }
    
    func selected(status statusID: String) {
        self.selected(status: statusID, state: .unknown)
    }
    
    func selected(status statusID: String, state: CollapseState) {
        show(ConversationViewController(for: statusID, state: state, mastodonController: apiController), sender: self)
    }
    
    func compose(editing draft: Draft?, animated: Bool = true, isDucked: Bool = false) {
        let draft = draft ?? apiController.createDraft()
        if UIDevice.current.userInterfaceIdiom == .pad || UIDevice.current.userInterfaceIdiom == .mac {
            let compose = UserActivityManager.editDraftActivity(id: draft.id, accountID: apiController.accountInfo!.id)
            let options = UIWindowScene.ActivationRequestOptions()
            options.preferredPresentationStyle = .prominent
            UIApplication.shared.requestSceneSessionActivation(nil, userActivity: compose, options: options, errorHandler: nil)
        } else {
            let compose = ComposeHostingController(draft: draft, mastodonController: apiController)
            if #available(iOS 16.0, *),
               presentDuckable(compose, animated: animated, isDucked: isDucked) {
                return
            } else {
                present(compose, animated: animated)
            }
        }
    }
    
    func compose(inReplyToID: String? = nil, mentioningAcct: String? = nil, animated: Bool = true) {
        let draft = apiController.createDraft(inReplyToID: inReplyToID, mentioningAcct: mentioningAcct)
        compose(editing: draft, animated: animated)
    }
    
    func showLoadingLargeImage(url: URL, cache: ImageCache, description: String?, animatingFrom sourceView: UIImageView) {
        let vc = LoadingLargeImageViewController(url: url, cache: cache, imageDescription: description)
        vc.animationSourceView = sourceView
        vc.transitioningDelegate = self
        present(vc, animated: true)
    }
    
    func gallery(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) -> GalleryViewController {
        let vc = GalleryViewController(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex)
        vc.transitioningDelegate = self
        return vc
    }
    
    func showGallery(attachments: [Attachment], sourceViews: [UIImageView?], startIndex: Int) {
        present(gallery(attachments: attachments, sourceViews: sourceViews, startIndex: startIndex), animated: true)
    }
    
    private func moreOptions(forURL url: URL) -> UIActivityViewController {
        let customActivites: [UIActivity] = [
            OpenInSafariActivity()
        ]
        let activityController = UIActivityViewController(activityItems: [url], applicationActivities: customActivites)
        activityController.completionWithItemsHandler = OpenInSafariActivity.completionHandler(navigator: self, url: url)
        return activityController
    }
    
    private func moreOptions(forStatus statusID: String) -> UIActivityViewController {
        guard let status = apiController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID)") }
        guard let url = status.url else {
            Logging.general.fault("Status missing URL: id=\(status.id, privacy: .public), reblog=\((status.reblog?.id).debugDescription, privacy: .public)")
            fatalError("Cannot create UIActivityViewController for status without URL")
        }
        
        return UIActivityViewController(activityItems: [url, StatusActivityItemSource(status)], applicationActivities: nil)
    }
    
    private func moreOptions(forAccount accountID: String) -> UIActivityViewController {
        guard let account = apiController.persistentContainer.account(for: accountID) else { fatalError("Missing cached account \(accountID)") }
        
        return UIActivityViewController(activityItems: [account.url, AccountActivityItemSource(account)], applicationActivities: nil)
    }
    
    func showMoreOptions(forStatus statusID: String, source: PopoverSource) {
        let vc = moreOptions(forStatus: statusID)
        source.apply(to: vc)
        present(vc, animated: true)
    }
    
    func showMoreOptions(forURL url: URL, source: PopoverSource) {
        let vc = moreOptions(forURL: url)
        source.apply(to: vc)
        present(vc, animated: true)
    }
    
    func showMoreOptions(forAccount accountID: String, source: PopoverSource) {
        let vc = moreOptions(forAccount: accountID)
        source.apply(to: vc)
        present(vc, animated: true)
    }
    
}

enum PopoverSource {
    case none
    case view(WeakHolder<UIView>)
    case barButtonItem(WeakHolder<UIBarButtonItem>)
    
    func apply(to viewController: UIViewController) {
        if let popoverPresentationController = viewController.popoverPresentationController {
            switch self {
            case .none:
                break
            case .view(let view):
                popoverPresentationController.sourceView = view.object
            case .barButtonItem(let item):
                popoverPresentationController.barButtonItem = item.object
            }
        }
    }
    
    static func view(_ view: UIView?) -> Self {
        .view(WeakHolder(view))
    }
    
    static func barButtonItem(_ item: UIBarButtonItem?) -> Self {
        .barButtonItem(WeakHolder(item))
    }
}

private let statusPathRegex = try! NSRegularExpression(
    pattern:
           "(^/@[a-z0-9_]+/\\d{18})"        // mastodon
        + "|(^/@.+@.+/\\d{18})"             // mastodon remote
        + "|(^/notice/[a-z0-9]{18})"        // pleroma
        + "|(^/notes/[a-z0-9]{10})"         // misskey
        + "|(^/p/[a-z0-9_]+/\\d{18})"       // pixelfed
        + "|(^/i/web/post/\\d{18})"         // pixelfed web frontend
        + "|(^/u/.+/h/[a-z0-9]{18})"        // honk
        + "|(^/@.+/statuses/[a-z0-9]{26})"  // gotosocial
    ,
    options: .caseInsensitive
)

private func isLikelyResolvableAsStatus(_ url: URL) -> Bool {
    let path = url.path
    let range = NSRange(location: 0, length: path.utf16.count)
    return statusPathRegex.numberOfMatches(in: path, range: range) == 1
}
