//
//  SelectablePlayerListView.swift
//  Padel Tournament
//
//  Created by Razmig Sarkissian on 10/02/2024.
//

import SwiftUI
import CoreData
import Combine
import LeStorage

typealias PlayerSelectionAction = ((Set<ImportedPlayer>) -> ())
typealias ContentUnavailableAction = ((SearchViewModel) -> ())

struct SelectablePlayerListView: View {
    let allowSelection: Int
    let playerSelectionAction: PlayerSelectionAction?
    let contentUnavailableAction: ContentUnavailableAction?
    
    @EnvironmentObject var dataStore: DataStore
    @Environment(ImportObserver.self) private var importObserver: ImportObserver

    @StateObject private var searchViewModel: SearchViewModel
    @Environment(\.dismiss) var dismiss
    
    @State private var searchText: String = ""

    init(allowSelection: Int = 0, isPresented: Bool = true, searchField: String? = nil, dataSet: DataSet = .national, filterOption: PlayerFilterOption = .all, hideAssimilation: Bool = false, ascending: Bool = true, sortOption: SortOption = .rank, fromPlayer: FederalPlayer? = nil, codeClub: String? = nil, ligue: String? = nil, showFemaleInMaleAssimilation: Bool = false, tokens: [SearchToken] = [], hidePlayers: [String]? = nil, lastDataSource: Bool = false, playerSelectionAction: PlayerSelectionAction? = nil, contentUnavailableAction: ContentUnavailableAction?  = nil) {
        self.allowSelection = allowSelection
        self.playerSelectionAction = playerSelectionAction
        self.contentUnavailableAction = contentUnavailableAction
        self.searchText = searchField ?? ""
        let searchViewModel = SearchViewModel()
        searchViewModel.tokens = tokens
        if lastDataSource {
            searchViewModel.mostRecentDate = DataStore.shared.appSettings.lastDataSourceDate()
        }

        searchViewModel.searchText = searchField ?? ""
        searchViewModel.debouncableText = searchField ?? ""
        searchViewModel.showFemaleInMaleAssimilation = showFemaleInMaleAssimilation
        searchViewModel.searchText = searchField ?? ""
        searchViewModel.isPresented = isPresented
        searchViewModel.allowSelection = allowSelection
        searchViewModel.codeClub = fromPlayer?.clubCode ?? codeClub
        searchViewModel.clubName = nil
        searchViewModel.ligueName = fromPlayer?.ligue ?? ligue
        searchViewModel.dataSet = dataSet
        searchViewModel.filterOption = fromPlayer == nil ? filterOption : fromPlayer!.isManPlayer ? .male : .female
        searchViewModel.hideAssimilation = hideAssimilation
        searchViewModel.ascending = ascending
        searchViewModel.sortOption = sortOption
        searchViewModel.hidePlayers = hidePlayers
        _searchViewModel = StateObject(wrappedValue: searchViewModel)
    }

    var enableSourceCheck: Binding<Bool> {
        Binding {
            searchViewModel.mostRecentDate != nil
        } set: { value in
            if value == false {
                searchViewModel.mostRecentDate = nil
            } else {
                searchViewModel.mostRecentDate = dataStore.appSettings.lastDataSourceDate()
            }
        }
    }
    
    var body: some View {
        VStack(spacing: 0) {
            if importObserver.isImportingFile() == false {
                if searchViewModel.filterSelectionEnabled == false {
                    VStack {
                        HStack {
                            Picker(selection: $searchViewModel.filterOption) {
                                ForEach(PlayerFilterOption.allCases, id: \.self) { scope in
                                    Text(scope.icon().capitalized)
                                }
                            } label: {
                            }
                            .pickerStyle(.segmented)
                            Menu {
                                if let lastDataSource = dataStore.appSettings.localizedLastDataSource() {
                                    Section {
                                        Toggle(isOn: enableSourceCheck) {
                                            Text("Limité à \(lastDataSource)")
                                        }
                                    }
                                }
                                Section {
                                    ForEach(SourceFileManager.getSortOption()) { option in
                                        Toggle(isOn: .init(get: {
                                            return searchViewModel.sortOption == option
                                        }, set: { value in
                                            if searchViewModel.sortOption == option {
                                                searchViewModel.ascending.toggle()
                                            }
                                            searchViewModel.sortOption = option
                                        })) {
                                            Label(option.localizedLabel(), systemImage: searchViewModel.sortOption == option ? (searchViewModel.ascending ? "chevron.up" : "chevron.down") : "")
                                        }
                                    }
                                } header: {
                                    Text("Trier par")
                                }
                                Divider()
                                
                                Section {
                                    Picker(selection: $searchViewModel.selectedAgeCategory) {
                                        ForEach(FederalTournamentAge.allCases) { ageCategory in
                                            Text(ageCategory.localizedFederalAgeLabel(.title)).tag(ageCategory)
                                        }
                                    } label: {
                                        Text("Catégorie d'âge")
                                    }
                                    
                                } header: {
                                    Text("Catégorie d'âge")
                                }
                                Divider()

                                Section {
                                    Toggle(isOn: .init(get: {
                                        return searchViewModel.hideAssimilation == false
                                    }, set: { value in
                                        searchViewModel.hideAssimilation.toggle()
                                    })) {
                                        Text("Afficher")
                                    }
                                    Toggle(isOn: .init(get: {
                                        return searchViewModel.hideAssimilation == true
                                    }, set: { value in
                                        searchViewModel.hideAssimilation.toggle()
                                    })) {
                                        Text("Masquer")
                                    }
                                } header: {
                                    Text("Assimilés")
                                }
                            } label: {
                                VStack(alignment: .trailing) {
                                    Label(searchViewModel.sortOption.localizedLabel(), systemImage: searchViewModel.ascending ? "chevron.up" : "chevron.down")
                                    if searchViewModel.selectedAgeCategory != .unlisted {
                                        Text(searchViewModel.selectedAgeCategory.localizedFederalAgeLabel()).font(.caption)
                                    }
                                }
                            }
                        }
                    }
                    .padding(.bottom)
                    .padding(.horizontal)
                    .background(Material.thick)
                    Divider()
                }
                MySearchView(searchViewModel: searchViewModel, contentUnavailableAction: contentUnavailableAction)
                    .environment(\.editMode, searchViewModel.allowMultipleSelection ? .constant(.active) : .constant(.inactive))
                    .searchable(text: $searchViewModel.debouncableText, tokens: $searchViewModel.tokens, suggestedTokens: $searchViewModel.suggestedTokens, isPresented: $searchViewModel.isPresented, placement: .navigationBarDrawer(displayMode: .always), prompt: searchViewModel.prompt(forDataSet: searchViewModel.dataSet), token: { token in
                        Text(token.shortLocalizedLabel)
                    })
                    .keyboardType(.alphabet)
                    .autocorrectionDisabled()
                //                .searchSuggestions({
                //                    ForEach(searchViewModel.suggestedTokens) { token in
                //                        Button {
                //                            searchViewModel.tokens.append(token)
                //                        } label: {
                //                            Label(token.localizedLabel(), systemImage:  token.icon())
                //                        }
                //                    }
                //                })
                
                    .onReceive(
                        searchViewModel.$debouncableText
                            .debounce(for: .seconds(searchViewModel.debounceTrigger), scheduler: DispatchQueue.main)
                            .throttle(for: .seconds(searchViewModel.throttleTrigger), scheduler: DispatchQueue.main, latest: true)
                    ) {
                        guard !$0.isEmpty else {
                            if searchViewModel.searchText.isEmpty == false {
                                searchViewModel.searchText = ""
                            }
                            return
                        }
                        print(">> searching for: \($0)")
                        searchViewModel.searchText = $0
                    }
                    .scrollDismissesKeyboard(.immediately)
                    .navigationBarBackButtonHidden(searchViewModel.allowMultipleSelection)
                    .toolbarBackground(searchViewModel.allowMultipleSelection ? .visible : .hidden, for: .bottomBar)
                    .toolbarBackground(.visible, for: .navigationBar)
//                    .toolbarRole(searchViewModel.allowMultipleSelection ? .navigationStack : .editor)
                    .interactiveDismissDisabled(searchViewModel.selectedPlayers.isEmpty == false)
                    .navigationTitle(searchViewModel.label(forDataSet: searchViewModel.dataSet))
                    .navigationBarTitleDisplayMode(.inline)
            } else {
                List {
                    
                }
            }
        }
        .id(importObserver.currentImportDate)
        .overlay {
            if let importedFile = SourceFileManager.shared.mostRecentDateAvailable, importObserver.isImportingFile() {
                ContentUnavailableView("Importation en cours", systemImage: "square.and.arrow.down", description: Text("Padel Club récupère les données de \(importedFile.monthYearFormatted)"))

            }
        }
        .onAppear {
            if searchViewModel.tokens.isEmpty && searchText.isEmpty {
                searchViewModel.debouncableText.removeAll()
                searchViewModel.searchText.removeAll()
            }
            searchViewModel.allowSelection = allowSelection
            searchViewModel.selectedPlayers.removeAll()
            searchViewModel.hideAssimilation = false
            searchViewModel.ascending = true
            searchViewModel.sortOption = .rank
            searchViewModel.suggestedTokens = searchViewModel.dataSet.tokens
//            searchViewModel.fromPlayer = nil
//            searchViewModel.codeClub = nil
//            searchViewModel.ligueName = nil
//            searchViewModel.user = nil
//            searchViewModel.dataSet = .national
//            searchViewModel.filterOption = .all
        }
        .onChange(of: searchViewModel.selectedPlayers) {
            
            if let playerSelectionAction, searchViewModel.selectionIsOver {
                playerSelectionAction(searchViewModel.selectedPlayers)
                dismiss()
            }
            
            if searchViewModel.tokens.isEmpty && searchViewModel.searchText.isEmpty == false {
                searchViewModel.debouncableText = ""
            }
            
            if searchViewModel.selectedPlayers.isEmpty && searchViewModel.filterSelectionEnabled {
                searchViewModel.filterSelectionEnabled = false
            } else if searchViewModel.allowSelection >= searchViewModel.selectedPlayers.count {
                searchViewModel.filterSelectionEnabled = true
            }
        }
        .onChange(of: searchViewModel.dataSet) {
            searchViewModel.suggestedTokens = searchViewModel.dataSet.tokens
            if searchViewModel.filterSelectionEnabled {
                searchViewModel.filterSelectionEnabled = false
            }
        }
        .onChange(of: searchViewModel.tokens) {
            if searchViewModel.tokens.first == .age {
                searchViewModel.selectedAgeCategory = .unlisted
            }
        }
        .toolbar {
            if searchViewModel.allowMultipleSelection {
                ToolbarItemGroup(placement: .topBarLeading) {
                    Button(role: .cancel) {
                        searchViewModel.selectedPlayers.removeAll()
                        dismiss()
                    } label: {
                        Text("Annuler")
                    }
                }
                
                ToolbarItem(placement: .topBarTrailing) {
                    ButtonValidateView {
                        if let playerSelectionAction {
                            playerSelectionAction(searchViewModel.selectedPlayers)
                        }
                        dismiss()
                    }
                    .disabled(searchViewModel.selectedPlayers.isEmpty)
                }
                ToolbarItem(placement: .status) {
                    let count = searchViewModel.selectedPlayers.count
                    VStack(spacing: 0) {
                        Text(count.formatted() + " joueur" + count.pluralSuffix + " séléctionné" + count.pluralSuffix).font(.footnote).foregroundStyle(.secondary)
                        FooterButtonView("\(searchViewModel.filterSelectionEnabled ? "masquer" : "voir") la liste") {
                            searchViewModel.filterSelectionEnabled.toggle()
                        }
                    }
                }
            }
        }
//        .modifierWithCondition(searchViewModel.user != nil) { thisView in
//            thisView
                .toolbarTitleMenu {
                    Picker(selection: $searchViewModel.dataSet) {
                        ForEach(DataSet.allCases) { dataSet in
                            Text(searchViewModel.label(forDataSet: dataSet)).tag(dataSet)
                        }
                    } label: {
                        
                    }
                }
//        }
//        .bottomBarAlternative(hide: searchViewModel.selectedPlayers.isEmpty) {
//            ZStack {
//                HStack{
//                    Button {
//                        searchViewModel.filterSelectionEnabled.toggle()
//                    } label: {
//                        if searchViewModel.filterSelectionEnabled {
//                            Image(systemName: "line.3.horizontal.decrease.circle.fill")
//                        } else {
//                            Image(systemName: "line.3.horizontal.decrease.circle")
//                        }
//                    }
//                    Spacer()
//                }
//                Button {
//                    if let playerSelectionAction {
//                        playerSelectionAction(searchViewModel.selectedPlayers)
//                    }
//                    dismiss()
//                } label: {
//                    Text("Ajouter le" + searchViewModel.selectedPlayers.count.pluralSuffix + " \(searchViewModel.selectedPlayers.count) joueur" + searchViewModel.selectedPlayers.count.pluralSuffix)
//                }
//                .buttonStyle(.borderedProminent)
//            }
//        }
    }
}


struct MySearchView: View {
    @Environment(\.isSearching) private var isSearching
    @Environment(\.dismissSearch) private var dismissSearch
    @Environment(\.editMode) var editMode
    @ObservedObject var searchViewModel: SearchViewModel

    @FetchRequest private var players: FetchedResults<ImportedPlayer>
    let contentUnavailableAction: ContentUnavailableAction?
    
    init(searchViewModel: SearchViewModel, contentUnavailableAction: ContentUnavailableAction? = nil) {
        self.contentUnavailableAction = contentUnavailableAction
        _searchViewModel = ObservedObject(wrappedValue: searchViewModel)
        _players = FetchRequest<ImportedPlayer>(sortDescriptors: searchViewModel.sortDescriptors(), predicate: searchViewModel.predicate())
    }
    
    func searchedPlayers() -> [ImportedPlayer] {
        if searchViewModel.searchText.isEmpty {
            return Array(players)
        }
        
        if let searchPredicate = searchViewModel.searchTextPredicate() {
            let filteredPlayers = players.filter { player in
                searchPredicate.evaluate(with: player)
            }
            return filteredPlayers
        }
        
        return Array(players)
    }

    var body: some View {
        playersView
            .overlay {
                overlayView()
            }
            .onChange(of: isSearching) {
                if isSearching && searchViewModel.filterSelectionEnabled {
                    searchViewModel.filterSelectionEnabled = false
                }
            }
            .onChange(of: searchViewModel.filterSelectionEnabled) {
                if searchViewModel.filterSelectionEnabled && isSearching {
                    dismissSearch()
                }
            }
            .listStyle(.grouped)
            .headerProminence(.increased)
            .scrollDismissesKeyboard(.immediately)
    }
    
    var specificBugFixUUID: String {
        if searchViewModel.dataSet == .national || searchViewModel.dataSet == .ligue {
            return UUID().uuidString
        } else {
            if searchViewModel.tokens.isEmpty && isSearching {
                return UUID().uuidString
            }
            return "specificBugFixUUID"
        }
    }
    
    @ViewBuilder
    var playersView: some View {
        if searchViewModel.allowMultipleSelection {
            List(selection: $searchViewModel.selectedPlayers) {
                if searchViewModel.filterSelectionEnabled {
                    let array = Array(searchViewModel.selectedPlayers)
                    Section {
                        ForEach(array) { player in
                            ImportedPlayerView(player: player, index: nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
                        }
                        .onDelete { indexSet in
                            for index in indexSet {
                                let p = array[index]
                                searchViewModel.selectedPlayers.remove(p)
                            }
                        }
                    } header: {
                        Text(searchViewModel.selectedPlayers.count.formatted() + " " + searchViewModel.filterOption.localizedPlayerLabel + searchViewModel.selectedPlayers.count.pluralSuffix)
                    }
                } else if (searchViewModel.isPresented == true && searchViewModel.dataSet == .national && searchViewModel.dataSet == .ligue && searchViewModel.searchText.isEmpty == true) {
                } else {
                    Section {
                        ForEach(players, id: \.self) { player in
                            ImportedPlayerView(player: player, index: nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
                        }
                    } header: {
                        if players.isEmpty == false {
                            headerView()
                        }
                    }
                }
            }
            .id(specificBugFixUUID)
        } else {
            List {
                if searchViewModel.dataSet == .national || searchViewModel.dataSet == .ligue {
                    if searchViewModel.allowSingleSelection {
                        Section {
                            ForEach(players) { player in
                                Button {
                                    searchViewModel.selectedPlayers.insert(player)
                                } label: {
                                    ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
                                        .contentShape(Rectangle())
                                }
                                .frame(maxWidth: .infinity)
                                .buttonStyle(.plain)
                            }
                        } header: {
                            if players.isEmpty == false {
                                headerView()
                            }
                        }
                        .id(UUID())
                    } else if searchViewModel.shouldIncludeSearchTextPredicate() {
                        Section {
                            ForEach(players.indices, id: \.self) { index in
                                let player = players[index]
                                ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
                            }
                        } header: {
                            if players.isEmpty == false {
                                headerView()
                            }
                        }
                        .id(UUID())
                    } else {
                        let filteredPlayers = searchedPlayers()

                        Section {
                            ForEach(filteredPlayers.indices, id: \.self) { index in
                                let player = filteredPlayers[index]
                                let realIndex = searchViewModel.showIndex() ? players.firstIndex(of: player) : nil
                                let computedIndex = realIndex != nil ? realIndex! + 1 : nil
                                ImportedPlayerView(player: player, index: computedIndex, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
                            }
                        } header: {
                            if filteredPlayers.isEmpty == false {
                                headerView()
                            }
                        }
                        .id(UUID())
                    }
                } else {
                    let filteredPlayers = searchedPlayers()
                    Section {
                        ForEach(filteredPlayers.indices, id: \.self) { index in
                            let player = filteredPlayers[index]
                            let realIndex = searchViewModel.showIndex() ? players.firstIndex(of: player) : nil
                            let computedIndex = realIndex != nil ? realIndex! + 1 : nil
                            if searchViewModel.allowSingleSelection {
                                Button {
                                    searchViewModel.selectedPlayers.insert(player)
                                } label: {
                                    ImportedPlayerView(player: player, index: computedIndex, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
                                        .contentShape(Rectangle())
                                }
                                .frame(maxWidth: .infinity)
                                .buttonStyle(.plain)
                            } else {
                                ImportedPlayerView(player: player, index: computedIndex, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation, showProgression: true)
                            }
                        }
                    } header: {
                        if filteredPlayers.isEmpty == false {
                            headerView()
                        }
                    }
                    .id(UUID())
                }
            }
        }
    }

    private func headerView() -> some View {
        HStack {
            Text("\(players.count.formatted()) \( searchViewModel.filterOption.localizedPlayerLabel)\( players.count.pluralSuffix)")
            Spacer()
        }
    }

    @ViewBuilder
    func overlayView() -> some View {
        if let token = searchViewModel.tokens.first, searchViewModel.searchText.isEmpty {
            ContentUnavailableView(token.titleLabel, systemImage: token.systemImage, description: Text(token.message))
        } else if players.isEmpty && searchViewModel.filterSelectionEnabled == false && searchViewModel.searchText.isEmpty == false {
            ContentUnavailableView {
                Label("Aucun résultat pour «\(searchViewModel.searchText)»", systemImage: "magnifyingglass")
            } description: {
                Text(searchViewModel.contentUnavailableMessage)
            } actions: {
                
                RowButtonView("Nouvelle recherche") {
                    searchViewModel.debouncableText = ""
                }
                .padding()
                if let contentUnavailableAction {
                    RowButtonView("Créer \(searchViewModel.searchText)") {
                        contentUnavailableAction(searchViewModel)
                    }
                    .padding()
                }
            }
        }
    }
}
