/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import Cocoa
import IGListDiffKit

final class UsersViewController: NSViewController {

    @IBOutlet weak var collectionView: NSCollectionView!

    // MARK: Data

    var users = [User]() {
        didSet {
            computeFilteredUsers()
        }
    }

    var searchTerm = "" {
        didSet {
            computeFilteredUsers()
        }
    }

    private func computeFilteredUsers() {
        guard !searchTerm.isEmpty else {
            filteredUsers = users
            return
        }

        filteredUsers = users.filter({ $0.name.localizedCaseInsensitiveContains(self.searchTerm) })
    }

    fileprivate func delete(user: User) {
        guard let index = self.users.firstIndex(where: { $0.pk == user.pk }) else { return }

        self.users.remove(at: index)
    }

    // MARK: -
    // MARK: Diffing

    var isFirstRun = true
    var filteredUsers = [User]() {
        didSet {
            // A crash occurs when you try to use performBatchUpdates the first time
            guard !isFirstRun else {
                collectionView.reloadData()
                isFirstRun = false
                return
            }

            // get the difference between the old array of Users and the new array of Users
            let diff = ListDiffPaths(fromSection: 0, toSection: 0, oldArray: oldValue, newArray: filteredUsers, option: .equality)
            let batchUpdates = diff.forBatchUpdates()
            let inserts = Set(batchUpdates.inserts)
            let deletes = Set(batchUpdates.deletes)
            let updates = Set(batchUpdates.updates)
            let moves = Set(batchUpdates.moves)

            // this difference is used here to update the collection view, but it can be used
            // to update collection views and other similar interface elements
            // this code can also be added to an extension of NSCollectionView ;)

            // Set the animation duration when updating the collection view
            NSAnimationContext.current.duration = 0.25

            // Perform the updates to the collection view
            collectionView.animator().performBatchUpdates({
                collectionView.deleteItems(at: deletes)
                collectionView.insertItems(at: inserts)
                collectionView.reloadItems(at: updates)
                moves.forEach { move in
                    collectionView.moveItem(at: move.from, to: move.to)
                }
            }, completionHandler: nil)
        }
    }

    // MARK: -

    private func loadSampleUsers() {
        guard let file = Bundle.main.url(forResource: "users", withExtension: "json") else { return }

        do {
            self.users = try UsersProvider(with: file).users
        } catch {
            NSAlert(error: error).runModal()
        }
    }

    // MARK: Interface

    override func viewDidLoad() {
        super.viewDidLoad()

        // The view needs to be backed by a CALayer to be able to enable the collections view animations you can
        // enable this by selecting the view controller's view in the Interface Builder in the Core Animation section
        // of the View Effects inspector tab, through code you can do by view.wantsLayer = true
        loadSampleUsers()
    }

    override func viewDidAppear() {
        super.viewDidAppear()

        view.window?.titleVisibility = .hidden
    }

    @IBAction func shuffle(_ sender: Any?) {
        users = users.shuffled
    }

    @IBAction func search(_ sender: NSSearchField) {
        searchTerm = sender.stringValue
    }
}

extension UsersViewController: UserCollectionViewCellDelegate {

    func itemDeleted(_ user: User) {
        self.delete(user: user)
    }
}

extension UsersViewController: NSCollectionViewDelegate {
}

extension UsersViewController: NSCollectionViewDataSource {

    private struct Storyboard {
        static let cellIdentifier = "UserCollectionViewCell"
    }

    func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.filteredUsers.count
    }

    @available(OSX 10.11, *)
    func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
        let item = collectionView.makeItem(withIdentifier: NSUserInterfaceItemIdentifier(rawValue: Storyboard.cellIdentifier), for: indexPath)
        guard let cell = item as? UserCollectionViewCell else { return item }

        cell.delegate = self
        cell.bindViewModel(filteredUsers[indexPath.item])
        return cell
    }
}

extension UsersViewController: NSCollectionViewDelegateFlowLayout {

    func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize {

        let availableWidth = collectionView.bounds.width
        return CGSize(width: availableWidth, height: 47)
    }
}
