import UIKit
import RxSwift
import RxCocoa
import RealmSwift
import PKHUD
import ExceptionCatcher

class SearchController: UIViewController, UISearchResultsUpdating, UITableViewDelegate, UIScrollViewDelegate {
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var showMapButton: UIBarButtonItem!
    
    private let bag = DisposeBag()
    private let searchController = UISearchController(searchResultsController: nil)
    private var refreshControl = UIRefreshControl()
    private var datasource: RxSectionedDataSource<Vehicle,VehicleCell>!
    private var isLoadingPage = false
    private var pageLoadingIndicator = UIActivityIndicatorView(style: .medium)
    
    var filterRelay = BehaviorRelay<Filter>(value: Filter())
    var filter = Filter()

    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.showMapButton.isEnabled = false
        
        searchController.searchResultsUpdater = self
        searchController.obscuresBackgroundDuringPresentation = false
        searchController.searchBar.placeholder = NSLocalizedString("Search plate numbers", comment: "")
        navigationItem.searchController = searchController
        definesPresentationContext = true
        
        self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: self.pageLoadingIndicator)
        
        //self.refreshControl.attributedTitle = NSAttributedString(string: "")
        self.refreshControl.addTarget(self, action: #selector(self.refresh(_:)), for: .valueChanged)
        self.tableView.addSubview(self.refreshControl)

        self.datasource = RxSectionedDataSource(table: self.tableView)
        self.tableView.delegate = self
        
        DispatchQueue.main.async {
            self.filterRelay
                //.throttle(.seconds(2), scheduler: MainScheduler.instance)
                .debounce(.milliseconds(500), scheduler: MainScheduler.instance)
                .do(onNext: { _ in self.pageLoadingIndicator.startAnimating() })
                .flatMap { Api.getVehicles(with: $0, pageToken: self.datasource.pageToken).do(onError: { print($0) }).catchErrorJustReturn(PagedResponse<Vehicle>()) }
                .observeOn(MainScheduler.instance)
                .do(onNext: {
                    if let count = $0.count {
                        self.navigationItem.title = String.localizedStringWithFormat(NSLocalizedString("vehicles found", comment: ""), count)
                        self.showMapButton.isEnabled = count > 0
                    }
                    self.refreshControl.endRefreshing()
                    self.isLoadingPage = false
                    self.pageLoadingIndicator.stopAnimating()
                })
                .bind(to: self.datasource.data)
                .disposed(by: self.bag)
        }
    }
    
    // FIXME: Code duplication
    func updateDetailController(with vehicle: Vehicle) {
        if let splitViewController = self.view.window?.rootViewController as? UISplitViewController
        {
            var detail: UINavigationController?
            if splitViewController.viewControllers.count == 2 {
                detail = splitViewController.viewControllers.last as? UINavigationController
            } else {
                let storyboard = UIStoryboard(name: "Main", bundle: nil)
                detail = storyboard.instantiateViewController(identifier: "ReportNavController")
            }
            
            if let detail = detail {
                detail.popToRootViewController(animated: true)
                let report = detail.viewControllers.first as? ReportController
                report?.vehicle = vehicle
                splitViewController.showDetailViewController(detail, sender: self)
                //self.performSegue(withIdentifier: "OpenDetailSegue", sender: self)
            }
        }
    }

    // MARK: - UISearchResultsUpdating
    
    func updateSearchResults(for searchController: UISearchController) {
        let newQuery = searchController.searchBar.text?.uppercased() ?? ""
        guard self.filter.searchString != newQuery else { return }
        
        self.filter.searchString = newQuery
        self.datasource.reset()
        self.filterRelay.accept(self.filter)
    }
    
    // MARK: -
    
    @IBAction func onFilter(_ sender: UIBarButtonItem) {
        let sb = UIStoryboard(name: "Main", bundle: nil)
        let controller = sb.instantiateViewController(identifier: "FiltersController") as FiltersController
        controller.filter = self.filter
        controller.onDone = {
            self.filter = controller.filter
            self.datasource.setSortParameter(self.filter.sortBy ?? .updatedDate)
            self.datasource.reset()
            self.filterRelay.accept(self.filter)
        }
        self.navigationController?.pushViewController(controller, animated: true)
    }
    
    @IBAction func showOnMap(_ sender: UIBarButtonItem) {
        let sb = UIStoryboard(name: "Main", bundle: nil)
        let controller = sb.instantiateViewController(identifier: "GlobalEventsNavigation") as UINavigationController
        if let eventsVC = controller.viewControllers.first as? GlobalEventsController {
            eventsVC.filter = self.filter
        }

        controller.modalPresentationStyle = .fullScreen
        //self.navigationController?.pushViewController(controller, animated: true)
        self.present(controller, animated: true)
    }
    
    @objc func refresh(_ sender: AnyObject) {
        self.showMapButton.isEnabled = false
        self.datasource.reset()
        self.filterRelay.accept(self.filter)
    }
    
    // MARK: - UITableViewDelegate
    
    func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
        let vehicle = self.datasource.item(at: indexPath)
        let updateAction = UIContextualAction(style: .normal, title: NSLocalizedString("Update", comment: "")) { action, view, completion in
            self.update(vehicle: vehicle, at: indexPath)
            completion(true)
        }
        updateAction.image = UIImage(systemName: "arrow.2.circlepath")
        updateAction.backgroundColor = .systemBlue
        
        let configuration = UISwipeActionsConfiguration(actions: [updateAction])
        configuration.performsFirstActionWithFullSwipe = false
        return configuration
    }
    
    func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
        let vehicle = self.datasource.item(at: indexPath)
        
        return UIContextMenuConfiguration(identifier: nil, previewProvider: nil) { _ in
            let update = UIAction(title: NSLocalizedString("Update", comment: ""), image: UIImage(systemName: "arrow.2.circlepath")) { action in
                self.update(vehicle: vehicle, at: indexPath)
            }

            return UIMenu(title: NSLocalizedString("Actions", comment: ""), children: [update])
        }
    }
    
    func update(vehicle: Vehicle, at indexPath: IndexPath) {
        HUD.show(.progress)
        Api.checkVehicle(by: vehicle.getNumber(), force: true).observeOn(MainScheduler.instance).subscribe { newVehicle in
            HUD.hide()
            do {
                let realm = try Realm()
                if realm.object(ofType: Vehicle.self, forPrimaryKey: vehicle.getNumber()) != nil {
                    try realm.write {
                        realm.add(newVehicle, update: .all)
                    }
                }
            } catch {
                print(error)
                self.show(error: error)
            }
            
            let frozenVehicle = newVehicle.realm != nil ? newVehicle.freeze() : newVehicle
            self.datasource.set(item: frozenVehicle, at: indexPath)
            self.updateDetailController(with: frozenVehicle)
        } onError: { err in
            HUD.show(error: err)
        }.disposed(by: self.bag)
    }
    
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        let vehicle = self.datasource.item(at: indexPath)
        self.updateDetailController(with: vehicle)
    }
    
    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard tableView.contentSize.height > 0 else { return }
        
        let toBottom = tableView.contentSize.height - (tableView.contentOffset.y + tableView.frame.size.height)
        if toBottom < 100 && !self.isLoadingPage && self.datasource.needMoreData() {
            self.isLoadingPage = true
            self.filterRelay.accept(self.filter)
        }
    }
}
