//
//  ProfileDirectoryViewController.swift
//  Tusker
//
//  Created by Shadowfacts on 2/6/21.
//  Copyright © 2021 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm

class ProfileDirectoryViewController: UIViewController {

    weak var mastodonController: MastodonController!
    
    private var collectionView: UICollectionView!
    private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
    
    private var scope: Scope = .everywhere
    private var order: DirectoryOrder = .active
    
    init(mastodonController: MastodonController) {
        self.mastodonController = mastodonController
        
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()

        title = NSLocalizedString("Profile Directory", comment: "profile directory title")
        
        let filterItem = UIBarButtonItem(image: UIImage(systemName: "line.3.horizontal.decrease.circle"), menu: nil)
        filterItem.accessibilityLabel = "Filter"
        navigationItem.rightBarButtonItem = filterItem
        updateFilterMenu()
        
        let layout = UICollectionViewCompositionalLayout(sectionProvider: { (sectionIndex, layoutEnvironment) in
            let itemHeight = NSCollectionLayoutDimension.absolute(200)
            let itemWidth: NSCollectionLayoutDimension
            if case .compact = layoutEnvironment.traitCollection.horizontalSizeClass {
                itemWidth = .fractionalWidth(1)
            } else {
                itemWidth = .absolute((layoutEnvironment.container.contentSize.width - 16 - 8 * 2) / 2)
            }
            
            let itemSize = NSCollectionLayoutSize(widthDimension: itemWidth, heightDimension: itemHeight)
            let item = NSCollectionLayoutItem(layoutSize: itemSize)
            let itemB = NSCollectionLayoutItem(layoutSize: itemSize)
            
            let groupSize = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: itemHeight)
            let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item, itemB])
            group.interItemSpacing = .flexible(16)
            
            let section = NSCollectionLayoutSection(group: group)
            section.interGroupSpacing = 16
            section.contentInsets = NSDirectionalEdgeInsets(top: 8, leading: 8, bottom: 8, trailing: 8)
            return section
        })
        
        collectionView = UICollectionView(frame: view.bounds, collectionViewLayout: layout)
        collectionView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        collectionView.backgroundColor = .appSecondaryBackground
        collectionView.register(UINib(nibName: "FeaturedProfileCollectionViewCell", bundle: .main), forCellWithReuseIdentifier: "featuredProfileCell")
        collectionView.delegate = self
        collectionView.dragDelegate = self
        collectionView.allowsFocus = true
        view.addSubview(collectionView)
        
        dataSource = createDataSource()
        updateProfiles()
    }
    
    private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
        let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { (collectionView, indexPath, item) in
            guard case let .account(account) = item else { fatalError() }
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "featuredProfileCell", for: indexPath) as! FeaturedProfileCollectionViewCell
            cell.updateUI(account: account)
            return cell
        }
        return dataSource
    }
    
    private func updateFilterMenu() {
        let scopeMenu = UIMenu(options: .displayInline, children: [
            UIAction(title: "Everywhere", subtitle: "Users from the whole network", image: UIImage(systemName: "globe"), state: scope == .everywhere ? .on : .off, handler: { [unowned self] _ in
                self.scope = .everywhere
                self.updateFilterMenu()
                self.updateProfiles()
            }),
            UIAction(title: mastodonController.accountInfo!.instanceURL.host!, subtitle: "Only users from your instance", image: UIImage(systemName: "house"), state: scope == .instance ? .on : .off, handler: { [unowned self] _ in
                self.scope = .instance
                self.updateFilterMenu()
                self.updateProfiles()
            }),
        ])
        let orderMenu = UIMenu(options: .displayInline, children: DirectoryOrder.allCases.map { order in
            UIAction(title: order.title, subtitle: order.subtitle, image: nil, state: self.order == order ? .on : .off) { [unowned self] _ in
                self.order = order
                self.updateFilterMenu()
                self.updateProfiles()
            }
        })
        navigationItem.rightBarButtonItem!.menu = UIMenu(children: [
            scopeMenu,
            orderMenu,
        ])
    }
    
    private func updateProfiles() {
        let scope = self.scope
        let order = self.order
        let local = scope == .instance
        let request = Client.getFeaturedProfiles(local: local, order: order)
        mastodonController.run(request) { (response) in
            guard case let .success(accounts, _) = response,
                  self.scope == scope,
                  self.order == order else {
                return
            }
            
            var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
            snapshot.appendSections([.featuredProfiles])
            snapshot.appendItems(accounts.map { .account($0) })
            DispatchQueue.main.async {
                self.dataSource.apply(snapshot)
            }
        }
    }

}

extension ProfileDirectoryViewController {
    enum Section {
        case featuredProfiles
    }
    enum Item: Hashable {
        case account(Account)
        
        func hash(into hasher: inout Hasher) {
            guard case let .account(account) = self else { return }
            hasher.combine(account.id)
        }
    }
}

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

extension ProfileDirectoryViewController: ToastableViewController {
}

extension ProfileDirectoryViewController: MenuActionProvider {
}

extension ProfileDirectoryViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard let item = dataSource.itemIdentifier(for: indexPath),
              case let .account(account) = item else {
            return
        }
        show(ProfileViewController(accountID: account.id, mastodonController: mastodonController), sender: nil)
    }
    
    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
        guard let item = dataSource.itemIdentifier(for: indexPath),
              case let .account(account) = item else {
            return nil
        }
        
        return UIContextMenuConfiguration(identifier: nil) {
            return ProfileViewController(accountID: account.id, mastodonController: self.mastodonController)
        } actionProvider: { (_) in
            let actions = self.actionsForProfile(accountID: account.id, source: .view(self.collectionView.cellForItem(at: indexPath)))
            return UIMenu(children: actions)
        }

    }
    
    func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
        if let viewController = animator.previewViewController {
            animator.preferredCommitStyle = .pop
            animator.addCompletion {
                self.show(viewController, sender: nil)
            }
        }
    }
}

extension ProfileDirectoryViewController: UICollectionViewDragDelegate {
    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        guard let item = dataSource.itemIdentifier(for: indexPath),
              case let .account(account) = item,
              let currentAccountID = mastodonController.accountInfo?.id else {
            return []
        }
        let provider = NSItemProvider(object: account.url as NSURL)
        let activity = UserActivityManager.showProfileActivity(id: account.id, accountID: currentAccountID)
        activity.displaysAuxiliaryScene = true
        provider.registerObject(activity, visibility: .all)
        return [UIDragItem(itemProvider: provider)]
    }
}

extension ProfileDirectoryViewController {
    enum Scope: CaseIterable {
        case instance, everywhere
    }
}

private extension DirectoryOrder {
    var title: String {
        switch self {
        case .active:
            return "Active"
        case .new:
            return "New"
        }
    }
    var subtitle: String {
        switch self {
        case .active:
            return "Users who have posted lately"
        case .new:
            return "Recently joined users"
        }
    }
}
