//
//  SearchDetailViewController.swift
//  Mastodon
//
//  Created by MainasuK Cirno on 2021-7-13.
//

import UIKit
import Combine
import MastodonAsset
import MastodonCore
import MastodonLocalization

final class CustomSearchController: UISearchController {
    
    let customSearchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: 300, height: 100))
    
    override var searchBar: UISearchBar { customSearchBar }
    
}

// Fake search bar not works on iPad with UISplitViewController
// check device and fallback to standard UISearchController
final class SearchDetailViewController: UIViewController, NeedsDependency {

    var disposeBag = Set<AnyCancellable>()
    var observations = Set<NSKeyValueObservation>()
    let searchResultOverviewCoordinator: SearchResultOverviewCoordinator

    weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
    weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }

    let isPhoneDevice: Bool = {
        return UIDevice.current.userInterfaceIdiom == .phone
    }()

    var viewModel: SearchDetailViewModel!

    let navigationBarVisualEffectBackgroundView = UIVisualEffectView(effect: UIBlurEffect(style: .systemMaterial))
    let navigationBarBackgroundView = UIView()
    let navigationBar: UINavigationBar = {
        let navigationItem = UINavigationItem()
        let barAppearance = UINavigationBarAppearance()
        barAppearance.configureWithTransparentBackground()
        navigationItem.standardAppearance = barAppearance
        navigationItem.compactAppearance = barAppearance
        navigationItem.scrollEdgeAppearance = barAppearance

        let navigationBar = UINavigationBar(
            frame: CGRect(x: 0, y: 0, width: 300, height: 100)
        )
        navigationBar.setItems([navigationItem], animated: false)
        return navigationBar
    }()
    
    let searchController: CustomSearchController = {
        let searchController = CustomSearchController()
        searchController.automaticallyShowsScopeBar = false
        searchController.obscuresBackgroundDuringPresentation = false
        return searchController
    }()
    private(set) lazy var searchBar: UISearchBar = {
        let searchBar: UISearchBar
        if isPhoneDevice {
            searchBar = UISearchBar(frame: CGRect(x: 0, y: 0, width: 320, height: 44))
        } else {
            searchBar = searchController.searchBar
            searchController.automaticallyShowsScopeBar = false
            searchController.searchBar.setShowsScope(true, animated: false)
        }
        searchBar.placeholder = L10n.Scene.Search.SearchBar.placeholder
        searchBar.sizeToFit()
        return searchBar
    }()

    private var searchHistoryViewController: SearchHistoryViewController

    private(set) lazy var searchResultsOverviewViewController: SearchResultsOverviewTableViewController = {
        return searchResultOverviewCoordinator.overviewViewController
    }()

    //MARK: - init

    init(appContext: AppContext, sceneCoordinator: SceneCoordinator, authContext: AuthContext) {
        self.context = appContext
        self.coordinator = sceneCoordinator

        self.searchResultOverviewCoordinator = SearchResultOverviewCoordinator(appContext: appContext, authContext: authContext, sceneCoordinator: sceneCoordinator)
        self.searchHistoryViewController = SearchHistoryViewController()
        searchHistoryViewController.context = appContext
        searchHistoryViewController.coordinator = sceneCoordinator
        searchHistoryViewController.viewModel = SearchHistoryViewModel(context: appContext, authContext: authContext)

        super.init(nibName: nil, bundle: nil)

        searchResultOverviewCoordinator.delegate = searchHistoryViewController
    }

    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

    //MARK: - UIViewController

    override func viewDidLoad() {

        searchResultOverviewCoordinator.start()

        super.viewDidLoad()

        setupBackgroundColor()

        setupSearchBar()
        
        addChild(searchHistoryViewController)
        searchHistoryViewController.view.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(searchHistoryViewController.view)
        searchHistoryViewController.didMove(toParent: self)
        if isPhoneDevice {
            NSLayoutConstraint.activate([
                searchHistoryViewController.view.topAnchor.constraint(equalTo: navigationBarBackgroundView.bottomAnchor),
                searchHistoryViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                searchHistoryViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                searchHistoryViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            ])
        } else {
            searchHistoryViewController.view.pinToParent()
        }

        addChild(searchResultsOverviewViewController)
        searchResultsOverviewViewController.view.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(searchResultsOverviewViewController.view)
        searchResultsOverviewViewController.didMove(toParent: self)
        if isPhoneDevice {
            NSLayoutConstraint.activate([
                searchResultsOverviewViewController.view.topAnchor.constraint(equalTo: navigationBarBackgroundView.bottomAnchor),
                searchResultsOverviewViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                searchResultsOverviewViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                searchResultsOverviewViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            ])
        } else {
            searchResultsOverviewViewController.view.pinToParent()
        }

        // bind search history display
        viewModel.searchText
            .removeDuplicates()
            .receive(on: DispatchQueue.main)
            .sink { [weak self] searchText in
                guard let self = self else { return }

                self.searchHistoryViewController.view.isHidden = !searchText.isEmpty
                self.searchResultsOverviewViewController.view.isHidden = searchText.isEmpty
            }
            .store(in: &disposeBag)
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        if isPhoneDevice {
            navigationController?.setNavigationBarHidden(true, animated: animated)
            searchBar.setShowsScope(true, animated: false)
            searchBar.setNeedsLayout()
            searchBar.layoutIfNeeded()
        } else {
            // do nothing
        }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)

        if isPhoneDevice {
            if !isModal {
                // prevent bar restore conflict with modal style issue
                navigationController?.setNavigationBarHidden(false, animated: animated)
            }
        } else {
            // do nothing
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        if isPhoneDevice {
            searchBar.setShowsCancelButton(true, animated: animated)
            UIView.performWithoutAnimation {
                self.searchBar.becomeFirstResponder()
            }
        } else {
            searchController.searchBar.setShowsCancelButton(true, animated: false)
            searchController.searchBar.setShowsScope(true, animated: false)
            UIView.performWithoutAnimation {
                self.searchController.isActive = true
            }
            DispatchQueue.main.async {
                self.searchController.searchBar.becomeFirstResponder()
            }
        }
    }
}

extension SearchDetailViewController {
    private func setupSearchBar() {
        if isPhoneDevice {
            navigationBar.translatesAutoresizingMaskIntoConstraints = false
            view.addSubview(navigationBar)
            NSLayoutConstraint.activate([
                navigationBar.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor),
                navigationBar.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                navigationBar.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            ])
            navigationBar.topItem?.titleView = searchBar
            navigationBar.layer.observe(\.bounds, options: [.new]) { [weak self] navigationBar, _ in
                guard let self = self else { return }
                self.viewModel.navigationBarFrame.value = navigationBar.frame
            }
            .store(in: &observations)
            
            navigationBarBackgroundView.translatesAutoresizingMaskIntoConstraints = false
            view.insertSubview(navigationBarBackgroundView, belowSubview: navigationBar)
            NSLayoutConstraint.activate([
                navigationBarBackgroundView.topAnchor.constraint(equalTo: view.topAnchor),
                navigationBarBackgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                navigationBarBackgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                navigationBarBackgroundView.bottomAnchor.constraint(equalTo: navigationBar.bottomAnchor),
            ])
            
            navigationBarVisualEffectBackgroundView.translatesAutoresizingMaskIntoConstraints = false
            view.insertSubview(navigationBarVisualEffectBackgroundView, belowSubview: navigationBarBackgroundView)
            navigationBarVisualEffectBackgroundView.pinTo(to: navigationBarBackgroundView)
        } else {
            navigationItem.setHidesBackButton(true, animated: false)
            navigationItem.titleView = nil
            navigationItem.searchController = searchController
            navigationItem.preferredSearchBarPlacement = .stacked
            searchController.searchBar.sizeToFit()
        }

        searchBar.delegate = self
    }

    private func setupBackgroundColor() {
        navigationBarBackgroundView.backgroundColor = SystemTheme.navigationBarBackgroundColor
        navigationBar.tintColor = Asset.Colors.Brand.blurple.color
    }
}

// MARK: - UISearchBarDelegate
extension SearchDetailViewController: UISearchBarDelegate {

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        let trimmedSearchText = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
        viewModel.searchText.value = trimmedSearchText

        searchResultsOverviewViewController.showStandardSearch(for: trimmedSearchText)
        searchResultsOverviewViewController.searchForSuggestions(for: trimmedSearchText)
    }

    func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
        guard let searchText = searchBar.text, searchText.isNotEmpty else { return }

        searchBar.resignFirstResponder()

        let searchResultViewModel = SearchResultViewModel(context: context, authContext: viewModel.authContext, searchScope: .all, searchText: searchText)
        coordinator.present(scene: .searchResult(viewModel: searchResultViewModel), transition: .show)
    }

    func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
        // dismiss or pop
        if isModal {
            dismiss(animated: true, completion: nil)
        } else {
            navigationController?.popViewController(animated: false)
        }
    }
}
