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

import UIKit
import Pachyderm

class SuggestedProfilesViewController: UIViewController, CollectionViewController {
    
    weak var mastodonController: MastodonController!
    
    var collectionView: UICollectionView!
    private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
    
    private var state = State.unloaded
    
    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 = "Suggested Accounts"
        
        let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in
            switch dataSource.sectionIdentifier(for: sectionIndex) {
            case nil:
                fatalError()
                
            case .loadingIndicator:
                var config = UICollectionLayoutListConfiguration(appearance: .grouped)
                config.backgroundColor = .appGroupedBackground
                config.showsSeparators = false
                return .list(using: config, layoutEnvironment: environment)
                
            case .accounts:
                let size = NSCollectionLayoutSize(widthDimension: .fractionalWidth(1), heightDimension: .estimated(280))
                let item = NSCollectionLayoutItem(layoutSize: size)
                let item2 = NSCollectionLayoutItem(layoutSize: size)
                let group = NSCollectionLayoutGroup.vertical(layoutSize: size, subitems: [item, item2])
                group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)
                group.interItemSpacing = .fixed(16)
                let section = NSCollectionLayoutSection(group: group)
                section.interGroupSpacing = 16
                section.contentInsets = NSDirectionalEdgeInsets(top: 16, leading: 0, bottom: 16, trailing: 0)
                return section
            }
        }
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        collectionView.delegate = self
        collectionView.dragDelegate = self
        collectionView.backgroundColor = .appGroupedBackground
        collectionView.allowsFocus = true
        view.addSubview(collectionView)
        NSLayoutConstraint.activate([
            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            collectionView.topAnchor.constraint(equalTo: view.topAnchor),
            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])
        
        dataSource = createDataSource()
    }
    
    private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
        let loadingCell = UICollectionView.CellRegistration<LoadingCollectionViewCell, Void> { cell, indexPath, itemIdentifier in
            cell.indicator.startAnimating()
        }
        let accountCell = UICollectionView.CellRegistration<SuggestedProfileCardCollectionViewCell, (String, Suggestion.Source)>(cellNib: UINib(nibName: "SuggestedProfileCardCollectionViewCell", bundle: .main)) { cell, indexPath, item in
            cell.delegate = self
            cell.updateUI(accountID: item.0, source: item.1)
        }
        return UICollectionViewDiffableDataSource(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
            switch itemIdentifier {
            case .loadingIndicator:
                return collectionView.dequeueConfiguredReusableCell(using: loadingCell, for: indexPath, item: ())
            case .account(let id, let source):
                return collectionView.dequeueConfiguredReusableCell(using: accountCell, for: indexPath, item: (id, source))
            }
        }
    }
    
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        Task {
            await loadInitial()
        }
    }
    
    @MainActor
    private func loadInitial() async {
        guard case .unloaded = state else {
            return
        }
        state = .loading
        
        var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
        snapshot.appendSections([.loadingIndicator])
        snapshot.appendItems([.loadingIndicator])
        await dataSource.apply(snapshot)
        
        do {
            let request = Client.getSuggestions(limit: 80)
            let (suggestions, _) = try await mastodonController.run(request)
            
            await mastodonController.persistentContainer.addAll(accounts: suggestions.map(\.account))
            
            var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
            snapshot.appendSections([.accounts])
            snapshot.appendItems(suggestions.map { .account($0.account.id, $0.source) })
            await dataSource.apply(snapshot)
            
            state = .loaded
        } catch {
            state = .unloaded
            let config = ToastConfiguration(from: error, with: "Error Loading Suggested Accounts", in: self) { [weak self] toast in
                toast.dismissToast(animated: true)
                await self?.loadInitial()
            }
            showToast(configuration: config, animated: true)
        }
    }
    
}

extension SuggestedProfilesViewController {
    enum State {
        case unloaded
        case loading
        case loaded
    }
}

extension SuggestedProfilesViewController {
    enum Section {
        case loadingIndicator
        case accounts
    }
    enum Item: Hashable {
        case loadingIndicator
        case account(String, Suggestion.Source)
    }
}

extension SuggestedProfilesViewController: UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, shouldSelectItemAt indexPath: IndexPath) -> Bool {
        if case .account(_, _) = dataSource.itemIdentifier(for: indexPath) {
            return true
        } else {
            return false
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        guard case .account(let id, _) = dataSource.itemIdentifier(for: indexPath) else {
            return
        }
        selected(account: id)
    }
    
    func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
        guard case .account(let id, _) = dataSource.itemIdentifier(for: indexPath),
              let cell = collectionView.cellForItem(at: indexPath) else {
            return nil
        }
        return UIContextMenuConfiguration {
            ProfileViewController(accountID: id, mastodonController: self.mastodonController)
        } actionProvider: { _ in
            UIMenu(children: self.actionsForProfile(accountID: id, source: .view(cell)))
        }
    }
    
    func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
        MenuPreviewHelper.willPerformPreviewAction(animator: animator, presenter: self)
    }
}

extension SuggestedProfilesViewController: UICollectionViewDragDelegate {
    func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
        guard case .account(let id, _) = dataSource.itemIdentifier(for: indexPath),
              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)
        provider.registerObject(activity, visibility: .all)
        return [UIDragItem(itemProvider: provider)]
    }
}

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

extension SuggestedProfilesViewController: MenuActionProvider {
}

extension SuggestedProfilesViewController: ToastableViewController {
}
