import UIKit
import DifferenceKit
import RxSwift
import RxCocoa

class  RxSectionedDataSource<Item,Cell>: NSObject, UITableViewDataSource where Item: Dated & Hashable & Differentiable & Decodable, Cell: UITableViewCell & ConfigurableCell, Cell.Item == Item {
    private var tv: UITableView
    private var cellIdentifier: String
    private var sections: [DateSection<Item>] = []
    private var items: [Item] = []
    private var sortParam: SortParameter = .updatedDate
    private(set) var pageToken: String? = nil
    private(set) var count: Int? = nil
    
    init(table: UITableView, cellIdentifier: String = String(describing: Cell.self)) {
        self.tv = table
        self.cellIdentifier = cellIdentifier
        super.init()
        self.tv.dataSource = self
    }
    
    // MARK: - UITableViewDataSource
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return self.sections.count
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return self.sections[section].elements.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: self.cellIdentifier, for: indexPath) as? Cell else {
            return UITableViewCell()
        }
        
        let item = self.sections[indexPath.section].elements[indexPath.row]
        cell.configure(with: item)
        return cell
    }
    
    func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
        return self.sections[section].header
    }
    
    // MARK: - Public
    
    func item(at indexPath: IndexPath) -> Item {
        return self.sections[indexPath.section].elements[indexPath.row]
    }
    
    func set(item: Item, at indexPath: IndexPath) {
        self.sections[indexPath.section].elements[indexPath.row] = item
    }
    
    var data: Binder<PagedResponse<Item>> {
        return Binder(self) { datasource, data in
            if let count = data.count {
                self.count = count
                self.items = data.items
            } else {
                self.items.append(contentsOf: data.items)
            }
            self.pageToken = data.pageToken
            
            DispatchQueue.global().async {
                let newSections = self.items.groupedByDate(type: self.sortParam)
                let changeset = StagedChangeset(source: self.sections, target: newSections)
                DispatchQueue.main.async {
                    self.tv.reload(using: changeset, with: .automatic) { newSects in
                        self.sections = newSects
                    }
                }
            }
        }
    }
    
    func needMoreData() -> Bool {
        guard let count = self.count else { return true }
        return self.items.count < count
    }
    
    func reset() {
        self.pageToken = nil
        self.count = nil
        self.items = []
    }
    
    func setSortParameter(_ param: SortParameter) {
        self.sortParam = param
    }
}
