//
//  EditListAccountsViewController.swift
//  Tusker
//
//  Created by Shadowfacts on 12/17/19.
//  Copyright © 2019 Shadowfacts. All rights reserved.
//

import UIKit
import Pachyderm
import Combine

class EditListAccountsViewController: EnhancedTableViewController {

    private var list: List
    let mastodonController: MastodonController
    
    var changedAccounts = false
    
    var dataSource: DataSource!
    var nextRange: RequestRange?
    
    var searchResultsController: SearchResultsViewController!
    var searchController: UISearchController!
    
    private var listRenamedCancellable: AnyCancellable?
    
    init(list: List, mastodonController: MastodonController) {
        self.list = list
        self.mastodonController = mastodonController
        
        super.init(style: .plain)
        
        listChanged()
        
        listRenamedCancellable = mastodonController.$lists
            .compactMap { $0.first { $0.id == list.id } }
            .removeDuplicates(by: { $0.title == $1.title })
            .sink { [unowned self] in
                self.list = $0
                self.listChanged()
            }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemeneted")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.register(UINib(nibName: "AccountTableViewCell", bundle: .main), forCellReuseIdentifier: "accountCell")
        
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 66
        tableView.allowsSelection = false
        tableView.backgroundColor = .appGroupedBackground
        
        dataSource = DataSource(tableView: tableView, cellProvider: { (tableView, indexPath, item) -> UITableViewCell? in
            guard case let .account(id) = item else { fatalError() }
            
            let cell = tableView.dequeueReusableCell(withIdentifier: "accountCell", for: indexPath) as! AccountTableViewCell
            cell.delegate = self
            cell.updateUI(accountID: id)
            cell.configurationUpdateHandler = { cell, state in
                cell.backgroundConfiguration = .appListGroupedCell(for: state)
            }
            return cell
        })
        dataSource.editListAccountsController = self
        
        searchResultsController = SearchResultsViewController(mastodonController: mastodonController, scope: .people)
        searchResultsController.following = true
        searchResultsController.delegate = self
        searchController = UISearchController(searchResultsController: searchResultsController)
        searchController.hidesNavigationBarDuringPresentation = false
        searchController.searchResultsUpdater = searchResultsController
        searchController.searchBar.autocapitalizationType = .none
        searchController.searchBar.placeholder = NSLocalizedString("Search accounts you follow", comment: "edit list search field placeholder")
        searchController.searchBar.delegate = searchResultsController
        definesPresentationContext = true
        
        navigationItem.searchController = searchController
        navigationItem.hidesSearchBarWhenScrolling = false
        if #available(iOS 16.0, *) {
            navigationItem.preferredSearchBarPlacement = .stacked
        }
        
        navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("Rename", comment: "rename list button title"), style: .plain, target: self, action: #selector(renameButtonPressed))
        
        Task {
            await loadAccounts()
        }
    }
    
    private func listChanged() {
        title = String(format: NSLocalizedString("Edit %@", comment: "edit list screen title"), list.title)
    }
    
    func loadAccounts() async {
        do {
            let request = List.getAccounts(list.id)
            let (accounts, pagination) = try await mastodonController.run(request)
            self.nextRange = pagination?.older
            
            await withCheckedContinuation { continuation in
                mastodonController.persistentContainer.addAll(accounts: accounts) {
                    continuation.resume()
                }
            }
            
            var snapshot = self.dataSource.snapshot()
            if snapshot.indexOfSection(.accounts) == nil {
                snapshot.appendSections([.accounts])
            } else {
                snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .accounts))
            }
            snapshot.appendItems(accounts.map { .account(id: $0.id) })
            await dataSource.apply(snapshot)
        } catch {
            let config = ToastConfiguration(from: error, with: "Error Loading Accounts", in: self) { [unowned self] toast in
                toast.dismissToast(animated: true)
                await self.loadAccounts()
            }
            self.showToast(configuration: config, animated: true)
        }
    }
    
    private func addAccount(id: String) async {
        changedAccounts = true
        do {
            let req = List.add(list.id, accounts: [id])
            _ = try await mastodonController.run(req)
            self.searchController.isActive = false
            await self.loadAccounts()
        } catch {
            let config = ToastConfiguration(from: error, with: "Error Adding Account", in: self) { [unowned self] toast in
                toast.dismissToast(animated: true)
                await self.addAccount(id: id)
            }
            self.showToast(configuration: config, animated: true)
        }
    }
    
    private func removeAccount(id: String) async {
        changedAccounts = true
        do {
            let request = List.remove(list.id, accounts: [id])
            _ = try await mastodonController.run(request)
            await self.loadAccounts()
        } catch {
            let config = ToastConfiguration(from: error, with: "Error Removing Account", in: self) { [unowned self] toast in
                toast.dismissToast(animated: true)
                await self.removeAccount(id: id)
            }
            self.showToast(configuration: config, animated: true)
        }
    }

    // MARK: - Table view delegate

    override func tableView(_ tableView: UITableView, editingStyleForRowAt indexPath: IndexPath) -> UITableViewCell.EditingStyle {
        return .delete
    }
    
    // MARK: - Interaction
    
    @objc func renameButtonPressed() {
        RenameListService(list: list, mastodonController: mastodonController, present: { self.present($0, animated: true) }).run()
    }
    
}

extension EditListAccountsViewController {
    enum Section: Hashable {
        case accounts
    }
    enum Item: Hashable {
        case account(id: String)
    }
    
    class DataSource: UITableViewDiffableDataSource<Section, Item> {
        weak var editListAccountsController: EditListAccountsViewController?
        
        override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
            return true
        }
        
        override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
            guard editingStyle == .delete,
                case let .account(id) = itemIdentifier(for: indexPath) else {
                return
            }
            
            Task {
                await self.editListAccountsController?.removeAccount(id: id)
            }
        }
    }
}

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

extension EditListAccountsViewController: ToastableViewController {
}

extension EditListAccountsViewController: MenuActionProvider {
}

extension EditListAccountsViewController: SearchResultsViewControllerDelegate {
    func selectedSearchResult(account accountID: String) {
        Task {
            await addAccount(id: accountID)
        }
    }
}
