//
//  TrendsViewController.swift
//  Tusker
//
//  Created by Shadowfacts on 2/5/23.
//  Copyright © 2023 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm
import SafariServices
import Combine

class TrendsViewController: UIViewController, CollectionViewController {
    
    let mastodonController: MastodonController
    
    var collectionView: UICollectionView!
    private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
    
    private var loadTask: Task<Void, Never>?
    private var trendingStatusesState = TrendingStatusesState.unloaded
    private let confirmLoadMoreStatuses = PassthroughSubject<Void, Never>()
    
    private var isShowingTrends = false
    private var shouldShowTrends: Bool {
        mastodonController.instanceFeatures.trends && !Preferences.shared.hideTrends
    }
    
    init(mastodonController: MastodonController) {
        self.mastodonController = mastodonController
        
        super.init(nibName: nil, bundle: nil)
        
        title = "Trends"
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in
            let sectionIdentifier = self.dataSource.snapshot().sectionIdentifiers[sectionIndex]
            switch sectionIdentifier {
            case .loadingIndicator:
                var listConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
                listConfig.backgroundColor = .appGroupedBackground
                listConfig.showsSeparators = false
                return .list(using: listConfig, layoutEnvironment: environment)
                
            case .trendingHashtags:
                var listConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
                listConfig.headerMode = .supplementary
                listConfig.footerMode = .supplementary
                listConfig.backgroundColor = .appGroupedBackground
                return .list(using: listConfig, layoutEnvironment: environment)
                
            case .trendingLinks:
                let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(280))
                let item = NSCollectionLayoutItem(layoutSize: itemSize)
                let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(250), heightDimension: .estimated(280))
                let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
                group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8)
                let section = NSCollectionLayoutSection(group: group)
                section.orthogonalScrollingBehavior = .groupPaging
                section.boundarySupplementaryItems = [
                    NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(12)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .topLeading),
                    NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(30)), elementKind: UICollectionView.elementKindSectionFooter, alignment: .bottomLeading),
                ]
                section.contentInsets = .zero
                return section

            case .profileSuggestions:
                let itemSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .absolute(250))
                let item = NSCollectionLayoutItem(layoutSize: itemSize)
                let groupSize = NSCollectionLayoutSize(widthDimension: .absolute(350), heightDimension: .absolute(250))
                let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
                group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 8, bottom: 0, trailing: 8)
                let section = NSCollectionLayoutSection(group: group)
                section.orthogonalScrollingBehavior = .groupPaging
                section.boundarySupplementaryItems = [
                    NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(12)), elementKind: UICollectionView.elementKindSectionHeader, alignment: .topLeading),
                    NSCollectionLayoutBoundarySupplementaryItem(layoutSize: NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(30)), elementKind: UICollectionView.elementKindSectionFooter, alignment: .bottomLeading),
                ]
                section.contentInsets = .zero
                return section
                
            case .trendingStatuses:
                var listConfig = UICollectionLayoutListConfiguration(appearance: .grouped)
                listConfig.headerMode = .supplementary
                listConfig.backgroundColor = .appGroupedBackground
                listConfig.itemSeparatorHandler = { [unowned self] indexPath, sectionConfig in
                    var config = sectionConfig
                    if let item = dataSource.itemIdentifier(for: indexPath),
                       item.hideListSeparators {
                        config.topSeparatorVisibility = .hidden
                        config.bottomSeparatorVisibility = .hidden
                    }
                    return config
                }
                return .list(using: listConfig, layoutEnvironment: environment)
            }
        }
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.delegate = self
        collectionView.dragDelegate = self
        collectionView.backgroundColor = .appGroupedBackground
        collectionView.allowsFocus = true
        view.addSubview(collectionView)
        
        dataSource = createDataSource()
        
        NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
    }
    
    private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
        let sectionHeaderCell = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { [unowned self] (headerView, collectionView, indexPath) in
            let section = self.dataSource.sectionIdentifier(for: indexPath.section)!
            var config = UIListContentConfiguration.groupedHeader()
            config.text = section.title
            headerView.contentConfiguration = config
        }
        let moreCell = UICollectionView.SupplementaryRegistration<MoreTrendsFooterCollectionViewCell>(elementKind: UICollectionView.elementKindSectionFooter) { [unowned self] supplementaryView, elementKind, indexPath in
            supplementaryView.delegate = self
            switch self.dataSource.sectionIdentifier(for: indexPath.section) {
            case nil, .loadingIndicator, .trendingStatuses:
                fatalError()
            case .trendingHashtags:
                supplementaryView.updateUI(.hashtags)
            case .trendingLinks:
                supplementaryView.updateUI(.links)
            case .profileSuggestions:
                supplementaryView.updateUI(.profileSuggestions)
            }
        }
        
        let loadingCell = UICollectionView.CellRegistration<LoadingCollectionViewCell, Void> { cell, indexPath, itemIdentifier in
            cell.indicator.startAnimating()
        }
        let trendingHashtagCell = UICollectionView.CellRegistration<TrendingHashtagCollectionViewCell, Hashtag> { (cell, indexPath, hashtag) in
            cell.updateUI(hashtag: hashtag)
        }
        let trendingLinkCell = UICollectionView.CellRegistration<TrendingLinkCardCollectionViewCell, Card>(cellNib: UINib(nibName: "TrendingLinkCardCollectionViewCell", bundle: .main)) { (cell, indexPath, card) in
            cell.updateUI(card: card)
        }
        let statusCell = UICollectionView.CellRegistration<TrendingStatusCollectionViewCell, (String, CollapseState)> { [unowned self] cell, indexPath, item in
            cell.delegate = self
            // TODO: filter trends
            cell.updateUI(statusID: item.0, state: item.1, filterResult: .allow, precomputedContent: nil)
        }
        let accountCell = UICollectionView.CellRegistration<SuggestedProfileCardCollectionViewCell, (String, Suggestion.Source)>(cellNib: UINib(nibName: "SuggestedProfileCardCollectionViewCell", bundle: .main)) { [unowned self] cell, indexPath, item in
            cell.delegate = self
            cell.updateUI(accountID: item.0, source: item.1)
        }
        let confirmLoadMoreCell = UICollectionView.CellRegistration<ConfirmLoadMoreCollectionViewCell, Bool> { [unowned self] cell, indexPath, isLoading in
            cell.confirmLoadMore = self.confirmLoadMoreStatuses
            cell.isLoading = isLoading
        }
        
        let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, item in
            switch item {
            case .loadingIndicator:
                return collectionView.dequeueConfiguredReusableCell(using: loadingCell, for: indexPath, item: ())
                
            case let .tag(hashtag):
                return collectionView.dequeueConfiguredReusableCell(using: trendingHashtagCell, for: indexPath, item: hashtag)
                
            case let .link(card):
                return collectionView.dequeueConfiguredReusableCell(using: trendingLinkCell, for: indexPath, item: card)
                
            case let .status(id, state):
                return collectionView.dequeueConfiguredReusableCell(using: statusCell, for: indexPath, item: (id, state))
                
            case let .account(id, source):
                return collectionView.dequeueConfiguredReusableCell(using: accountCell, for: indexPath, item: (id, source))
                
            case let .confirmLoadMoreStatuses(loading):
                return collectionView.dequeueConfiguredReusableCell(using: confirmLoadMoreCell, for: indexPath, item: loading)
            }
        }
        dataSource.supplementaryViewProvider = { (collectionView, elementKind, indexPath) in
            if elementKind == UICollectionView.elementKindSectionHeader {
                return collectionView.dequeueConfiguredReusableSupplementary(using: sectionHeaderCell, for: indexPath)
            } else if elementKind == UICollectionView.elementKindSectionFooter {
                return collectionView.dequeueConfiguredReusableSupplementary(using: moreCell, for: indexPath)
            } else {
                return nil
            }
        }
        return dataSource
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        clearSelectionOnAppear(animated: animated)
        
        if loadTask == nil {
            loadTask = Task(priority: .userInitiated) {
                if (try? await mastodonController.getOwnInstance()) != nil {
                    await loadTrends()
                }
            }
        }
    }
    
    @MainActor
    private func loadTrends() async {
        guard isShowingTrends != shouldShowTrends else {
            return
        }
        isShowingTrends = shouldShowTrends
        guard shouldShowTrends else {
            await dataSource.apply(NSDiffableDataSourceSnapshot())
            return
        }
        
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.loadingIndicator])
        snapshot.appendItems([.loadingIndicator])
        await apply(snapshot: snapshot)
        
        snapshot = NSDiffableDataSourceSnapshot()

        let hashtagsReq = Client.getTrendingHashtags(limit: 5)
        let hashtags = try? await mastodonController.run(hashtagsReq).0

        if let hashtags {
            snapshot.appendSections([.trendingHashtags])
            snapshot.appendItems(hashtags.map { .tag($0) }, toSection: .trendingHashtags)
        }
        
        if mastodonController.instanceFeatures.profileSuggestions {
            let req = Client.getSuggestions(limit: 10)
            let suggestions = try? await mastodonController.run(req).0
            if let suggestions {
                snapshot.appendSections([.profileSuggestions])
                await mastodonController.persistentContainer.addAll(accounts: suggestions.map(\.account))
                snapshot.appendItems(suggestions.map { .account($0.account.id, $0.source) }, toSection: .profileSuggestions)
            }
        }

        if mastodonController.instanceFeatures.trendingStatusesAndLinks {
            let linksReq = Client.getTrendingLinks(limit: 10)
            async let links = try? mastodonController.run(linksReq).0
            let statusesReq = Client.getTrendingStatuses(limit: 10)
            async let statuses = try? mastodonController.run(statusesReq).0

            if let links = await links {
                if snapshot.sectionIdentifiers.contains(.profileSuggestions) {
                    snapshot.insertSections([.trendingLinks], beforeSection: .profileSuggestions)
                } else {
                    snapshot.appendSections([.trendingLinks])
                }
                snapshot.appendItems(links.map { .link($0) }, toSection: .trendingLinks)
            }

            if let statuses = await statuses {
                await mastodonController.persistentContainer.addAll(statuses: statuses)
                snapshot.appendSections([.trendingStatuses])
                snapshot.appendItems(statuses.map { .status($0.id, .unknown) }, toSection: .trendingStatuses)
            }
        }

        if !Task.isCancelled {
            await apply(snapshot: snapshot)
            if snapshot.sectionIdentifiers.contains(.trendingStatuses) {
                self.trendingStatusesState = .loaded
            } else {
                self.trendingStatusesState = .unloaded
            }
        }
    }
    
    @MainActor
    private func loadOlderStatuses() async {
        guard case .loaded = trendingStatusesState else {
            return
        }
        trendingStatusesState = .loadingOlder
        
        let origSnapshot = dataSource.snapshot()
        var snapshot = origSnapshot
        if Preferences.shared.disableInfiniteScrolling {
            snapshot.appendItems([.confirmLoadMoreStatuses(false)], toSection: .trendingStatuses)
            await apply(snapshot: snapshot)
            
            for await _ in confirmLoadMoreStatuses.values {
                break
            }
            
            snapshot.deleteItems([.confirmLoadMoreStatuses(false)])
            snapshot.appendItems([.confirmLoadMoreStatuses(true)], toSection: .trendingStatuses)
            await apply(snapshot: snapshot, animatingDifferences: false)
        } else {
            snapshot.appendItems([.loadingIndicator], toSection: .trendingStatuses)
            await apply(snapshot: snapshot)
        }
        
        do {
            let request = Client.getTrendingStatuses(offset: origSnapshot.itemIdentifiers(inSection: .trendingStatuses).count)
            let (statuses, _) = try await mastodonController.run(request)
            
            await mastodonController.persistentContainer.addAll(statuses: statuses)
            
            var snapshot = origSnapshot
            snapshot.appendItems(statuses.map { .status($0.id, .unknown) }, toSection: .trendingStatuses)
            await apply(snapshot: snapshot)
        } catch {
            await apply(snapshot: origSnapshot)
            let config = ToastConfiguration(from: error, with: "Error Loading More Trending Statuses", in: self) { [weak self] toast in
                toast.dismissToast(animated: true)
                await self?.loadOlderStatuses()
            }
            showToast(configuration: config, animated: true)
        }
        
        trendingStatusesState = .loaded
    }
    
    @objc private func preferencesChanged() {
        if isShowingTrends != shouldShowTrends {
            loadTask?.cancel()
            loadTask = Task {
                await loadTrends()
            }
        }
    }
    
    private func apply(snapshot: NSDiffableDataSourceSnapshot<Section, Item>, animatingDifferences: Bool = true) async {
        await Task { @MainActor in
            self.dataSource.apply(snapshot, animatingDifferences: animatingDifferences)
        }.value
    }
    
    @MainActor
    private func removeProfileSuggestion(accountID: String) async {
        let req = Suggestion.remove(accountID: accountID)
        do {
            _ = try await mastodonController.run(req)
            var snapshot = dataSource.snapshot()
            // the source here doesn't matter, since it's ignored by the equatable and hashable impls
            snapshot.deleteItems([.account(accountID, .global)])
            await apply(snapshot: snapshot)
        } catch {
            let config = ToastConfiguration(from: error, with: "Error Removing Suggestion", in: self) { [unowned self] toast in
                toast.dismissToast(animated: true)
                _ = await self.removeProfileSuggestion(accountID: accountID)
            }
            self.showToast(configuration: config, animated: true)
        }
    }
}

extension TrendsViewController {
    enum TrendingStatusesState {
        case unloaded
        case loaded
        case loadingOlder
    }
}

extension TrendsViewController {
    enum Section {
        case loadingIndicator
        case trendingHashtags
        case trendingLinks
        case profileSuggestions
        case trendingStatuses
        
        var title: String? {
            switch self {
            case .loadingIndicator:
                return nil
            case .trendingHashtags:
                return "Trending Hashtags"
            case .trendingLinks:
                return "Trending Links"
            case .trendingStatuses:
                return "Trending Posts"
            case .profileSuggestions:
                return "Suggested Accounts"
            }
        }
    }
    enum Item: Equatable, Hashable {
        case loadingIndicator
        case status(String, CollapseState)
        case tag(Hashtag)
        case link(Card)
        case account(String, Suggestion.Source)
        case confirmLoadMoreStatuses(Bool)
        
        static func == (lhs: Item, rhs: Item) -> Bool {
            switch (lhs, rhs) {
            case (.loadingIndicator, .loadingIndicator):
                return true
            case let (.status(a, _), .status(b, _)):
                return a == b
            case let (.tag(a), .tag(b)):
                return a == b
            case let (.link(a), .link(b)):
                return a.url == b.url
            case let (.account(a, _), .account(b, _)):
                return a == b
            case (.confirmLoadMoreStatuses(let a), .confirmLoadMoreStatuses(let b)):
                return a == b
            default:
                return false
            }
        }
        
        func hash(into hasher: inout Hasher) {
            switch self {
            case .loadingIndicator:
                hasher.combine("loadingIndicator")
            case let .status(id, _):
                hasher.combine("status")
                hasher.combine(id)
            case let .tag(tag):
                hasher.combine("tag")
                hasher.combine(tag.name)
            case let .link(card):
                hasher.combine("link")
                hasher.combine(card.url)
            case let .account(id, _):
                hasher.combine("account")
                hasher.combine(id)
            case let .confirmLoadMoreStatuses(loading):
                hasher.combine("confirmLoadMoreStatuses")
                hasher.combine(loading)
            }
        }
        
        var shouldSelect: Bool {
            switch self {
            case .loadingIndicator, .confirmLoadMoreStatuses(_):
                return false
            default:
                return true
            }
        }
        
        var hideListSeparators: Bool {
            switch self {
            case .loadingIndicator, .confirmLoadMoreStatuses(_):
                return true
            default:
                return false
            }
        }
    }
}

extension TrendsViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
        if case .trendingStatuses = dataSource.sectionIdentifier(for: indexPath.section),
           indexPath.row == collectionView.numberOfItems(inSection: indexPath.section) - 1 {
            Task {
                await self.loadOlderStatuses()
            }
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
        return dataSource.itemIdentifier(for: indexPath)?.shouldSelect ?? false
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let item = dataSource.itemIdentifier(for: indexPath) else {
            return
        }
        switch item {
        case .loadingIndicator, .confirmLoadMoreStatuses(_):
            return
            
        case let .tag(hashtag):
            show(HashtagTimelineViewController(for: hashtag, mastodonController: mastodonController), sender: nil)
            
        case let .link(card):
            if let url = URL(card.url) {
                selected(url: url)
            }
            
        case let .status(id, state):
            selected(status: id, state: state.copy())
            
        case let .account(id, _):
            selected(account: id)
        }
    }
    
    @available(iOS, obsoleted: 16.0)
    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
        guard let item = dataSource.itemIdentifier(for: indexPath) else {
            return nil
        }
        
        switch item {
        case .loadingIndicator, .confirmLoadMoreStatuses(_):
            return nil
            
        case let .tag(hashtag):
            return UIContextMenuConfiguration(identifier: nil) {
                HashtagTimelineViewController(for: hashtag, mastodonController: self.mastodonController)
            } actionProvider: { (_) in
                UIMenu(children: self.actionsForHashtag(hashtag, source: .view(self.collectionView.cellForItem(at: indexPath))))
            }
            
        case let .link(card):
            guard let url = URL(card.url) else {
                return nil
            }
            let cell = collectionView.cellForItem(at: indexPath)!
            return UIContextMenuConfiguration {
                let vc = SFSafariViewController(url: url)
                vc.preferredControlTintColor = Preferences.shared.accentColor.color
                return vc
            } actionProvider: { _ in
                UIMenu(children: self.actionsForTrendingLink(card: card, source: .view(cell)))
            }
            
        case let .status(id, state):
            guard let status = mastodonController.persistentContainer.status(for: id) else {
                return nil
            }
            let cell = collectionView.cellForItem(at: indexPath)!
            return UIContextMenuConfiguration {
                ConversationViewController(for: id, state: state.copy(), mastodonController: self.mastodonController)
            } actionProvider: { _ in
                UIMenu(children: self.actionsForStatus(status, source: .view(cell)))
            }
            
        case let .account(id, _):
            let cell = collectionView.cellForItem(at: indexPath)!
            return UIContextMenuConfiguration {
                ProfileViewController(accountID: id, mastodonController: self.mastodonController)
            } actionProvider: { _ in
                let dismiss = UIAction(title: "Remove Suggestion", image: UIImage(systemName: "trash"), attributes: .destructive) { [unowned self] _ in
                    Task {
                        await self.removeProfileSuggestion(accountID: id)
                    }
                }
                return UIMenu(children: [UIMenu(options: .displayInline, children: [dismiss])] + self.actionsForProfile(accountID: id, source: .view(cell)))
            }
        }
    }
    
    // implementing the highlightPreviewForItemAt method seems to prevent the old, single IndexPath variant of this method from being called on iOS 16
    @available(iOS 16.0, *)
    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemsAt indexPaths: [IndexPath], point: CGPoint) -> UIContextMenuConfiguration? {
        guard indexPaths.count == 1 else {
            return nil
        }
        return self.collectionView(collectionView, contextMenuConfigurationForItemAt: indexPaths[0], point: point)
    }
    
    func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
        MenuPreviewHelper.willPerformPreviewAction(animator: animator, presenter: self)
    }
    
    func collectionView(_ collectionView: UICollectionView, contextMenuConfiguration configuration: UIContextMenuConfiguration, highlightPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? {
        switch dataSource.itemIdentifier(for: indexPath) {
        case .link(_), .account(_, _):
            guard let cell = collectionView.cellForItem(at: indexPath) else {
                return nil
            }
            let params = UIPreviewParameters()
            params.visiblePath = UIBezierPath(roundedRect: cell.bounds, cornerRadius: cell.contentView.layer.cornerRadius)
            return UITargetedPreview(view: cell, parameters: params)

        default:
            return nil
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, contextMenuConfiguration configuration: UIContextMenuConfiguration, dismissalPreviewForItemAt indexPath: IndexPath) -> UITargetedPreview? {
        return self.collectionView(collectionView, contextMenuConfiguration: configuration, highlightPreviewForItemAt: indexPath)
    }
}

extension TrendsViewController: UICollectionViewDragDelegate {
    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        guard let item = dataSource.itemIdentifier(for: indexPath) else {
                  return []
              }
        switch item {
        case .loadingIndicator, .confirmLoadMoreStatuses(_):
            return []
            
        case let .tag(hashtag):
            guard let url = URL(hashtag.url) else {
                return []
            }
            let provider = NSItemProvider(object: url as NSURL)
            if let activity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtag.name), accountID: mastodonController.accountInfo!.id) {
                activity.displaysAuxiliaryScene = true
                provider.registerObject(activity, visibility: .all)
            }
            return [UIDragItem(itemProvider: provider)]
            
        case let .link(card):
            guard let url = URL(card.url) else {
                return []
            }
            return [UIDragItem(itemProvider: NSItemProvider(object: url as NSURL))]
            
        case let .status(id, _):
            guard let status = mastodonController.persistentContainer.status(for: id),
                  let url = status.url else {
                return []
            }
            let provider = NSItemProvider(object: url as NSURL)
            let activity = UserActivityManager.showConversationActivity(mainStatusID: id, accountID: mastodonController.accountInfo!.id)
            activity.displaysAuxiliaryScene = true
            provider.registerObject(activity, visibility: .all)
            return [UIDragItem(itemProvider: provider)]
            
        case let .account(id, _):
            guard let account = mastodonController.persistentContainer.account(for: id) else {
                return []
            }
            let provider = NSItemProvider(object: account.url as NSURL)
            let activity = UserActivityManager.showProfileActivity(id: id, accountID: mastodonController.accountInfo!.id)
            activity.displaysAuxiliaryScene = true
            provider.registerObject(activity, visibility: .all)
            return [UIDragItem(itemProvider: provider)]
        }
    }
}

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

extension TrendsViewController: ToastableViewController {
}

extension TrendsViewController: MenuActionProvider {
}

extension TrendsViewController: StatusCollectionViewCellDelegate {
    func statusCellNeedsReconfigure(_ cell: StatusCollectionViewCell, animated: Bool, completion: (() -> Void)?) {
        if let indexPath = collectionView.indexPath(for: cell) {
            var snapshot = dataSource.snapshot()
            snapshot.reconfigureItems([dataSource.itemIdentifier(for: indexPath)!])
            dataSource.apply(snapshot, animatingDifferences: animated, completion: completion)
        }
    }
    
    func statusCellShowFiltered(_ cell: StatusCollectionViewCell) {
        // TODO: filtering
    }
}
