//
//  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
    @StateObject private var searchViewModel: SearchViewModel
    @Environment(\.dismiss) var dismiss
    
    var lastDataSource: String? {
        dataStore.appSettings.lastDataSource
    }
    
    @AppStorage("importingFiles") var importingFiles: Bool = false

    @State private var searchText: String = ""
    var mostRecentDate: Date? {
        guard let lastDataSource else { return nil }
        return URL.importDateFormatter.date(from: lastDataSource)
    }
    
    init(allowSelection: Int = 0, 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, playerSelectionAction: PlayerSelectionAction? = nil, contentUnavailableAction: ContentUnavailableAction?  = nil) {
        self.allowSelection = allowSelection
        self.playerSelectionAction = playerSelectionAction
        self.contentUnavailableAction = contentUnavailableAction
        self.searchText = searchField ?? ""
        let searchViewModel = SearchViewModel()
        searchViewModel.tokens = tokens
        searchViewModel.searchText = searchField ?? ""
        searchViewModel.debouncableText = searchField ?? ""
        searchViewModel.showFemaleInMaleAssimilation = showFemaleInMaleAssimilation
        searchViewModel.searchText = searchField ?? ""
        searchViewModel.isPresented = allowSelection != 0
        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 body: some View {
        VStack(spacing: 0) {
            if importingFiles == false {
                if searchViewModel.filterSelectionEnabled == false {
                    Picker(selection: $searchViewModel.filterOption) {
                        ForEach(PlayerFilterOption.allCases, id: \.self) { scope in
                            Text(scope.icon().capitalized)
                        }
                    } label: {
                    }
                    .pickerStyle(.segmented)
                    .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(.visible, for: .bottomBar)
//                    .toolbarRole(searchViewModel.allowMultipleSelection ? .navigationStack : .editor)
                    .interactiveDismissDisabled(searchViewModel.selectedPlayers.isEmpty == false)
                    .navigationTitle(searchViewModel.label(forDataSet: searchViewModel.dataSet))
                    .navigationBarTitleDisplayMode(.inline)
            } else {
                List {
                    
                }
            }
        }
        .id(importingFiles)
        .overlay {
            if let importedFile = SourceFileManager.shared.mostRecentDateAvailable, importingFiles {
                ContentUnavailableView("Importation en cours", systemImage: "square.and.arrow.down", description: Text("Padel Club récupère les données de \(importedFile.monthYearFormatted)"))

            }
        }
        .onAppear {
            searchViewModel.mostRecentDate = mostRecentDate
            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 {
                searchViewModel.filterSelectionEnabled = true
            }
        }
        .onChange(of: searchViewModel.dataSet) {
            searchViewModel.suggestedTokens = searchViewModel.dataSet.tokens
            if searchViewModel.filterSelectionEnabled {
                searchViewModel.filterSelectionEnabled = false
            }
        }
        .toolbar {
            if searchViewModel.allowMultipleSelection {
                ToolbarItemGroup(placement: .topBarLeading) {
                    Button(role: .cancel) {
                        searchViewModel.selectedPlayers.removeAll()
                        dismiss()
                    } label: {
                        Text("Annuler")
                    }
                }
                
                if searchViewModel.selectedPlayers.isEmpty == false {
                    ToolbarItem(placement: .topBarTrailing) {
                        ButtonValidateView {
                            if let playerSelectionAction {
                                playerSelectionAction(searchViewModel.selectedPlayers)
                            }
                            dismiss()
                        }
                    }
                    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())
    }

    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 {
            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, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation)
                        }
                        .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.searchText.isEmpty == true) {
                } else {
                    Section {
                        ForEach(players, id: \.self) { player in
                            ImportedPlayerView(player: player, index: nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation)
                        }
                    } header: {
                        if players.isEmpty == false {
                            headerView()
                        }
                    }
                }
            }
            .id(specificBugFixUUID)
        } else {
            List {
                if searchViewModel.dataSet == .national {
                    if searchViewModel.allowSingleSelection {
                        Section {
                            ForEach(players) { player in
                                Button {
                                    searchViewModel.selectedPlayers.insert(player)
                                } label: {
                                    ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation)
                                }
                                .buttonStyle(.plain)
                            }
                        } header: {
                            if players.isEmpty == false {
                                headerView()
                            }
                        }
                        .id(UUID())
                    } else {
                        Section {
                            ForEach(players) { player in
                                ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation)
                            }
                        } header: {
                            if players.isEmpty == false {
                                headerView()
                            }
                        }
                        .id(UUID())
                    }
                } else {
                    Section {
                        ForEach(players.indices, id: \.self) { index in
                            let player = players[index]
                            if searchViewModel.allowSingleSelection {
                                Button {
                                    searchViewModel.selectedPlayers.insert(player)
                                } label: {
                                    ImportedPlayerView(player: player, index: searchViewModel.showIndex() ? (index + 1) : nil, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation)
                                        .contentShape(Rectangle())
                                }
                                .frame(maxWidth: .infinity)
                                .buttonStyle(.plain)
                            } else {
                                ImportedPlayerView(player: player, showFemaleInMaleAssimilation: searchViewModel.showFemaleInMaleAssimilation)
                            }
                        }
                    } header: {
                        if players.isEmpty == false {
                            headerView()
                        }
                    }
                    .id(UUID())
                }
            }
        }
    }

    private func headerView() -> some View {
        HStack {
            Text("\(players.count.formatted()) \( searchViewModel.filterOption.localizedPlayerLabel)\( players.count.pluralSuffix)")
            Spacer()
            Menu {
                Section {
                    ForEach(SortOption.allCases) { 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 {
                    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: {
                Label(searchViewModel.sortOption.localizedLabel(), systemImage: searchViewModel.ascending ? "chevron.up" : "chevron.down")
            }
        }
    }

    @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: {
                
                Button {
                    searchViewModel.debouncableText = ""
                } label: {
                    Text("lancer une nouvelle recherche")
                }
                if let contentUnavailableAction {
                    Button {
                        contentUnavailableAction(searchViewModel)
                    } label: {
                        Text("créer \(searchViewModel.searchText)")
                    }
                }
            }
        }
    }
}
