//
//  SearchViewModel.swift
//  Padel Tournament
//
//  Created by Razmig Sarkissian on 07/02/2024.
//

import SwiftUI

class DebouncableViewModel: ObservableObject {
    @Published var debouncableText: String = ""
    var debounceTrigger: Double = 0.15
}

class SearchViewModel: ObservableObject, Identifiable {
    let id: UUID = UUID()
    var allowSelection: Int = 0
    var codeClub: String? = nil
    var clubName: String? = nil
    var ligueName: String? = nil
    var showFemaleInMaleAssimilation: Bool = false
    var hidePlayers: [String]?

    @Published var debouncableText: String = ""
    @Published var searchText: String = ""
    @Published var task: DispatchWorkItem?
    @Published var computedSearchText: String = ""
    @Published var tokens = [SearchToken]()
    @Published var suggestedTokens = [SearchToken]()
    @Published var dataSet: DataSet = .national
    @Published var filterOption = PlayerFilterOption.all
    @Published var hideAssimilation: Bool = false
    @Published var ascending: Bool = true
    @Published var sortOption: SortOption = .rank
    @Published var selectedPlayers: Set<ImportedPlayer> = Set()
    @Published var filterSelectionEnabled: Bool = false
    @Published var isPresented: Bool = false
    @Published var selectedAgeCategory: FederalTournamentAge = .unlisted
    @Published var mostRecentDate: Date? = nil

    var selectionIsOver: Bool {
        if allowSingleSelection && selectedPlayers.count == 1 {
            return true
        } else if allowMultipleSelection && selectedPlayers.count == allowSelection {
            return true
        }

        return false
    }

    var allowMultipleSelection: Bool {
        allowSelection > 1 || allowSelection == -1
    }

    var allowSingleSelection: Bool {
        allowSelection == 1
    }

    var debounceTrigger: Double {
        (dataSet == .national || dataSet == .ligue) ? 0.4 : 0.1
    }

    var throttleTrigger: Double {
        (dataSet == .national || dataSet == .ligue) ? 0.15 : 0.1
    }

    var contentUnavailableMessage: String {
        var message = ["Vérifiez l'ortographe ou lancez une nouvelle recherche."]
        if tokens.isEmpty {
            message.append(
                "Il est possible que cette personne n'est joué aucun tournoi depuis les 12 derniers mois,  dans ce cas, Padel Club ne pourra pas le trouver."
            )
        }
        return message.joined(separator: "\n")
    }

    func codeClubs() -> [String] {
        let clubs: [Club] = DataStore.shared.user.clubsObjects()
        return clubs.compactMap { $0.code }
    }

    func getCodeClub() -> String? {
        if let codeClub { return codeClub }
        if let userCodeClub = DataStore.shared.user.currentPlayerData()?.clubCode {
            return userCodeClub
        }
        return nil
    }

    func getLigueName() -> String? {
        if let ligueName { return ligueName }
        if let userLigueName = DataStore.shared.user.currentPlayerData()?.ligueName {
            return userLigueName
        }
        return nil
    }

    func shouldIncludeSearchTextPredicate() -> Bool {
        if allowMultipleSelection {
            return true
        }

        if allowSingleSelection {
            return true
        }

        if tokens.isEmpty == false || hideAssimilation || selectedAgeCategory != .unlisted {
            return true
        }

        return dataSet == .national && searchText.isEmpty == false
            && (tokens.isEmpty == true && hideAssimilation == false
                && selectedAgeCategory == .unlisted)
    }

    func showIndex() -> Bool {
        if dataSet == .national {
            if searchText.isEmpty == false
                && (tokens.isEmpty == true && hideAssimilation == false
                    && selectedAgeCategory == .unlisted)
            {
                return false
            } else {
                return isFiltering()
            }
        }
        if dataSet == .ligue { return isFiltering() }
        if filterOption == .all { return isFiltering() }
        return true
    }

    func isFiltering() -> Bool {
        searchText.isEmpty == false || tokens.isEmpty == false || hideAssimilation
            || selectedAgeCategory != .unlisted
    }

    func prompt(forDataSet: DataSet) -> String {
        switch forDataSet {
        case .national:
            if let mostRecentDate {
                return "base fédérale \(mostRecentDate.monthYearFormatted)"
            } else {
                return "rechercher"
            }
        case .ligue:
            return "dans cette ligue"
        case .club:
            return "dans ce club"
        case .favoriteClubs, .favoritePlayers:
            return "dans mes favoris"
        }
    }

    func label(forDataSet: DataSet) -> String {
        switch forDataSet {
        case .national:
            return "National"
        case .ligue:
            return (ligueName)?.capitalized ?? "Ma ligue"
        case .club:
            return (clubName)?.capitalized ?? "Mon club"
        case .favoriteClubs:
            return "Clubs favoris"
        case .favoritePlayers:
            return "Joueurs favoris"
        }
    }

    func words() -> [String] {
        let cleanedText = searchText.cleanSearchText().canonicalVersionWithPunctuation.trimmed
        return cleanedText.components(
            separatedBy: .whitespaces)
    }

    func wordsPredicates() -> NSPredicate? {
        let words = words().filter({ $0.isEmpty == false })

        // Handle special case of hyphenated words
        let hyphenatedWords = searchText.components(separatedBy: .whitespaces)
            .filter { $0.contains("-") }

        var predicates: [NSPredicate] = []

        // Add predicates for hyphenated words
        for word in hyphenatedWords {
            predicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", word))
            let parts = word.components(separatedBy: "-")
            for part in parts where part.count > 1 {
                predicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", part))
            }
        }

        // Regular words processing
        switch words.count {
        case 2:
            predicates.append(contentsOf: [
                NSPredicate(
                    format:
                        "canonicalLastName CONTAINS[cd] %@ AND canonicalFirstName CONTAINS[cd] %@",
                    words[0], words[1]),
                NSPredicate(
                    format:
                        "canonicalLastName CONTAINS[cd] %@ AND canonicalFirstName CONTAINS[cd] %@",
                    words[1], words[0]),
                // For multi-word first names, try the two words as a first name
                NSPredicate(
                    format: "canonicalFirstName CONTAINS[cd] %@", words.joined(separator: " ")),
            ])
        case 3:
            // Handle potential cases like "Jean Christophe CROS"
            predicates.append(contentsOf: [
                // First two words as first name, last as last name
                NSPredicate(
                    format:
                        "canonicalFirstName CONTAINS[cd] %@ AND canonicalLastName CONTAINS[cd] %@",
                    words[0] + " " + words[1], words[2]),
                // First as first name, last two as last name
                NSPredicate(
                    format:
                        "canonicalFirstName CONTAINS[cd] %@ AND canonicalLastName CONTAINS[cd] %@",
                    words[0], words[1] + " " + words[2]),
                // Last as first name, first two as last name
                NSPredicate(
                    format:
                        "canonicalFirstName CONTAINS[cd] %@ AND canonicalLastName CONTAINS[cd] %@",
                    words[2], words[0] + " " + words[1]),
            ])
        default:
            if words.count > 0 {
                // For single word or many words, try matching against full name
                predicates.append(
                    NSPredicate(
                        format: "canonicalFullName CONTAINS[cd] %@",
                        words.joined(separator: " ")))
            }
        }

        return predicates.isEmpty
            ? nil : NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
    }

    func searchTextPredicate() -> NSPredicate? {
        var predicates: [NSPredicate] = []
        let allowedCharacterSet = CharacterSet.alphanumerics.union(.whitespaces).union(
            CharacterSet(charactersIn: "-"))
        let canonicalVersionWithoutPunctuation = searchText.canonicalVersion
            .components(separatedBy: allowedCharacterSet.inverted)
            .joined()
            .trimmed

        // Check for license numbers
        let matches = canonicalVersionWithoutPunctuation.licencesFound()
        let licensesPredicates = matches.map { NSPredicate(format: "license contains[cd] %@", $0) }
        predicates.append(NSCompoundPredicate(orPredicateWithSubpredicates: licensesPredicates))

        if canonicalVersionWithoutPunctuation.isEmpty == false {
            let wordsPredicates = wordsPredicates()
            if let wordsPredicates {
                predicates.append(wordsPredicates)
            }

            // Add match for full name
            predicates.append(
                NSPredicate(
                    format: "canonicalFullName contains[cd] %@", canonicalVersionWithoutPunctuation)
            )

            // Add pattern match for more flexible matching
            let components = canonicalVersionWithoutPunctuation.split(separator: " ")
            let pattern = components.joined(separator: ".*")
            predicates.append(NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern))

//            // Look for exact matches on first or last name
//            let words = canonicalVersionWithoutPunctuation.components(separatedBy: .whitespaces)
//            for word in words where word.count > 2 {
//                predicates.append(
//                    NSPredicate(
//                        format: "firstName CONTAINS[cd] %@ OR lastName CONTAINS[cd] %@", word, word)
//                )
//            }
        }

        if predicates.isEmpty {
            return nil
        }
        return NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
    }

    func orPredicate() -> NSPredicate? {
        var predicates: [NSPredicate] = []
        let allowedCharacterSet = CharacterSet.alphanumerics.union(.whitespaces).union(
            CharacterSet(charactersIn: "-"))
        let canonicalVersionWithoutPunctuation = searchText.canonicalVersion
            .components(separatedBy: allowedCharacterSet.inverted)
            .joined()
            .trimmed
        let canonicalVersionWithPunctuation = searchText.canonicalVersionWithPunctuation.trimmed

        if tokens.isEmpty {
            if shouldIncludeSearchTextPredicate(),
                canonicalVersionWithoutPunctuation.isEmpty == false
            {
                if let searchTextPredicate = searchTextPredicate() {
                    predicates.append(searchTextPredicate)
                }
            }
        }

        // Process tokens
        for token in tokens {
            switch token {
            case .ligue:
                if canonicalVersionWithoutPunctuation.isEmpty {
                    predicates.append(NSPredicate(format: "ligueName == nil"))
                } else {
                    predicates.append(
                        NSPredicate(
                            format: "ligueName contains[cd] %@", canonicalVersionWithoutPunctuation)
                    )
                }
            case .club:
                if canonicalVersionWithoutPunctuation.isEmpty {
                    predicates.append(NSPredicate(format: "clubName == nil"))
                } else {
                    predicates.append(
                        NSPredicate(
                            format: "clubName contains[cd] %@", canonicalVersionWithoutPunctuation))
                }
            case .rankMoreThan:
                if canonicalVersionWithoutPunctuation.isEmpty
                    || Int(canonicalVersionWithoutPunctuation) == 0
                {
                    predicates.append(NSPredicate(format: "rank == 0"))
                } else {
                    predicates.append(
                        NSPredicate(format: "rank >= %@", canonicalVersionWithoutPunctuation))
                }
            case .rankLessThan:
                if canonicalVersionWithoutPunctuation.isEmpty
                    || Int(canonicalVersionWithoutPunctuation) == 0
                {
                    predicates.append(NSPredicate(format: "rank == 0"))
                } else {
                    predicates.append(
                        NSPredicate(format: "rank <= %@", canonicalVersionWithoutPunctuation))
                }
            case .rankBetween:
                let values = canonicalVersionWithPunctuation.components(separatedBy: ",")
                if canonicalVersionWithPunctuation.isEmpty || values.count != 2 {
                    predicates.append(NSPredicate(format: "rank == 0"))
                } else {
                    predicates.append(
                        NSPredicate(format: "rank BETWEEN {%@,%@}", values.first!, values.last!))
                }
            case .age:
                if canonicalVersionWithoutPunctuation.isEmpty
                    || Int(canonicalVersionWithoutPunctuation) == 0
                {
                    predicates.append(NSPredicate(format: "birthYear == 0"))
                } else if let birthYear = Int(canonicalVersionWithoutPunctuation) {
                    predicates.append(
                        NSPredicate(format: "birthYear == %@", birthYear.formattedAsRawString()))
                }
            }
        }

        if predicates.isEmpty {
            return nil
        }
        
        
        let full = NSCompoundPredicate(orPredicateWithSubpredicates: predicates)
        return full
    }

    func predicate() -> NSPredicate? {
        var predicates: [NSPredicate?] = [
            orPredicate(),
            filterOption == .male ? NSPredicate(format: "male == YES") : nil,
            filterOption == .female ? NSPredicate(format: "male == NO") : nil,
        ]

        if let mostRecentDate {
            predicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg))
        }

        if hideAssimilation {
            predicates.append(NSPredicate(format: "assimilation == %@", "Non"))
        }

        if selectedAgeCategory != .unlisted {
            let computedBirthYear = selectedAgeCategory.computedBirthYear()
            if let left = computedBirthYear.0 {
                predicates.append(
                    NSPredicate(format: "birthYear >= %@", left.formattedAsRawString()))
            }
            if let right = computedBirthYear.1 {
                predicates.append(
                    NSPredicate(format: "birthYear <= %@", right.formattedAsRawString()))
            }

        }

        switch dataSet {
        case .national:
            break
        case .ligue:
            if let ligueName = getLigueName() {
                predicates.append(NSPredicate(format: "ligueName == %@", ligueName))
            } else {
                predicates.append(NSPredicate(format: "ligueName == nil"))
            }
        case .club:
            if let codeClub = getCodeClub() {
                predicates.append(NSPredicate(format: "clubCode == %@", codeClub))
            } else {
                predicates.append(NSPredicate(format: "clubCode == nil"))
            }
        case .favoriteClubs:
            predicates.append(NSPredicate(format: "clubCode IN %@", codeClubs()))
        case .favoritePlayers:
            //todo
            predicates.append(NSPredicate(format: "license == nil"))
        }

        if hidePlayers?.isEmpty == false {
            predicates.append(NSPredicate(format: "NOT (license IN %@)", hidePlayers!))
        }

        return NSCompoundPredicate(andPredicateWithSubpredicates: predicates.compactMap({ $0 }))
    }

    func sortDescriptors() -> [SortDescriptor<ImportedPlayer>] {
        sortOption.sortDescriptors(ascending, dataSet: dataSet)
    }

    func nsSortDescriptors() -> [NSSortDescriptor] {
        sortDescriptors().map { NSSortDescriptor($0) }
    }

    static func getSpecialSlashPredicate(inputString: String) -> NSPredicate? {
        // Define a regular expression to find slashes between alphabetic characters (not digits)
        print(inputString)
        let cleanedInput = inputString.cleanSearchText()
        
        let pattern = /(\b[A-Za-z]+)\s*\/\s*([A-Za-z]+\b)/

        // Find matches in the input string
        guard let match = cleanedInput.firstMatch(of: pattern) else {
            print("No valid name pairs found")
            return nil
        }

        let lastName1 = match.output.1.trimmingCharacters(in: .whitespacesAndNewlines)
        let lastName2 = match.output.2.trimmingCharacters(in: .whitespacesAndNewlines)

        // Ensure both names are not empty
        guard !lastName1.isEmpty && !lastName2.isEmpty else {
            print("One or both names are empty")
            return nil
        }

        // Create the NSPredicate for searching in the `lastName` field
        let predicate = NSPredicate(
            format: "lastName CONTAINS[cd] %@ OR lastName CONTAINS[cd] %@", lastName1, lastName2)

        // Output the result
        //print("Generated Predicate: \(predicate)")
        return predicate
    }

    static func pastePredicate(
        pasteField: String, mostRecentDate: Date?, filterOption: PlayerFilterOption
    ) -> NSPredicate? {
        var andPredicates = [NSPredicate]()
        var orPredicates = [NSPredicate]()

        // Check for license numbers
        let matches = pasteField.licencesFound()
        let licensesPredicates = matches.map { NSPredicate(format: "license contains[cd] %@", $0) }
        orPredicates = licensesPredicates

        if matches.count == 2 {
            return NSCompoundPredicate(orPredicateWithSubpredicates: orPredicates)
        }

        // Add gender filter if specified
        if filterOption == .female {
            andPredicates.append(NSPredicate(format: "male == NO"))
        } else if filterOption == .male {
            andPredicates.append(NSPredicate(format: "male == YES"))
        }

        // Add date filter if specified
        if let mostRecentDate {
            andPredicates.append(NSPredicate(format: "importDate == %@", mostRecentDate as CVarArg))
        }

        // Check for slashes (representing alternatives)
        if let slashPredicate = getSpecialSlashPredicate(inputString: pasteField) {
            orPredicates.append(slashPredicate)
        }

        // Prepare text for processing - preserve hyphens but remove digits
        var text =
            pasteField
            .replacingOccurrences(of: "[\\(\\)\\[\\]\\{\\}]", with: "", options: .regularExpression)
            .replacingOccurrences(of: "/", with: " ")  // Replace slashes with spaces
            .trimmingCharacters(in: .whitespacesAndNewlines)

        // Remove digits
        let digitPattern = /\b\w*\d\w*\b/
        text = text.replacing(digitPattern, with: "").trimmingCharacters(
            in: .whitespacesAndNewlines)

        // Split text by whitespace to get potential name components
        let textComponents = text.components(separatedBy: .whitespacesAndNewlines)
            .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
            .filter {
                !$0.isEmpty && $0.count > 1 && !["de", "la", "le", "du"].contains($0.lowercased())
            }

        if textComponents.count < 50 {
            // Handle exact fullname match
            let fullName = textComponents.joined(separator: " ")
            if !fullName.isEmpty {
                orPredicates.append(
                    NSPredicate(format: "canonicalFullName CONTAINS[cd] %@", fullName))
            }

            // Handle hyphenated last names
            let hyphenatedComponents = textComponents.filter { $0.contains("-") }
            for component in hyphenatedComponents {
                orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", component))

                // Also search for each part of the hyphenated name
                let parts = component.components(separatedBy: "-")
                for part in parts {
                    if part.count > 1 {
                        orPredicates.append(NSPredicate(format: "lastName CONTAINS[cd] %@", part))
                    }
                }
            }

            // Try different combinations for first/last name
            if textComponents.count > 1 {
                // Try each pair of components as first+last and last+first
                for i in 0..<textComponents.count {
                    for j in 0..<textComponents.count where i != j {
                        orPredicates.append(
                            NSPredicate(
                                format:
                                    "(firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@) OR (firstName CONTAINS[cd] %@ AND lastName CONTAINS[cd] %@)",
                                textComponents[i], textComponents[j], textComponents[j],
                                textComponents[i]
                            ))

                        // Also try beginswith for more precise matches
                        orPredicates.append(
                            NSPredicate(
                                format:
                                    "(firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@) OR (firstName BEGINSWITH[cd] %@ AND lastName BEGINSWITH[cd] %@)",
                                textComponents[i], textComponents[j], textComponents[j],
                                textComponents[i]
                            ))
                    }
                }
            } else if textComponents.count == 1 {
                // If only one component, search in both first and last name
                orPredicates.append(
                    NSPredicate(
                        format: "firstName CONTAINS[cd] %@ OR lastName CONTAINS[cd] %@",
                        textComponents[0], textComponents[0]))
            }

            // Add pattern match for canonical full name
            let pattern = textComponents.joined(separator: ".*")
            orPredicates.append(NSPredicate(format: "canonicalFullName MATCHES[c] %@", pattern))
        }

        // Construct final predicate
        var predicate = NSCompoundPredicate(andPredicateWithSubpredicates: andPredicates)

        if !orPredicates.isEmpty {
            let orCompoundPredicate = NSCompoundPredicate(
                orPredicateWithSubpredicates: orPredicates)
            predicate = NSCompoundPredicate(andPredicateWithSubpredicates: [
                predicate, orCompoundPredicate,
            ])
        }

        return predicate
    }

}

enum SearchToken: String, CaseIterable, Identifiable {
    case club = "club"
    case ligue = "ligue"
    case rankMoreThan = "rang >"
    case rankLessThan = "rang <"
    case rankBetween = "rang <>"
    case age = "âge sportif"

    var id: String {
        rawValue
    }

    var message: String {
        switch self {
        case .club:
            return
                "Taper le nom d'un club pour y voir tous les joueurs ayant déjà joué un tournoi dans les 12 derniers mois."
        case .ligue:
            return
                "Taper le nom d'une ligue pour y voir tous les joueurs ayant déjà joué un tournoi dans les 12 derniers mois."
        case .rankMoreThan:
            return
                "Taper un nombre pour chercher les joueurs ayant un classement supérieur ou égale."
        case .rankLessThan:
            return
                "Taper un nombre pour chercher les joueurs ayant un classement inférieur ou égale."
        case .rankBetween:
            return
                "Taper deux nombres séparés par une virgule pour chercher les joueurs dans cette intervalle de classement"
        case .age:
            return "Taper une année de naissance"
        }
    }

    var titleLabel: String {
        switch self {
        case .club:
            return "Chercher un club"
        case .ligue:
            return "Chercher une ligue"
        case .rankMoreThan, .rankLessThan:
            return "Chercher un rang"
        case .rankBetween:
            return "Chercher une intervalle de classement"
        case .age:
            return "Chercher une année de naissance"
        }
    }

    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .club:
            return "Club"
        case .ligue:
            return "Ligue"
        case .rankMoreThan:
            return "Rang supérieur ou égale à"
        case .rankLessThan:
            return "Rang inférieur ou égale à"
        case .rankBetween:
            return "Rang entre deux valeurs"
        case .age:
            return "Année de naissance"
        }
    }

    var shortLocalizedLabel: String {
        switch self {
        case .club:
            return "Club"
        case .ligue:
            return "Ligue"
        case .rankMoreThan:
            return "Rang ≥"
        case .rankLessThan:
            return "Rang ≤"
        case .rankBetween:
            return "Rang ≥,≤"
        case .age:
            return "Né(e) en"
        }
    }

    func icon() -> String {
        switch self {
        case .club:
            return "house.and.flag.fill"
        case .ligue:
            return "house.and.flag.fill"
        case .rankMoreThan:
            return "figure.racquetball"
        case .rankLessThan:
            return "figure.racquetball"
        case .rankBetween:
            return "figure.racquetball"
        case .age:
            return "figure.racquetball"
        }
    }

    var systemImage: String {
        switch self {
        case .club:
            return "house.and.flag.fill"
        case .ligue:
            return "house.and.flag.fill"
        case .rankMoreThan:
            return "figure.racquetball"
        case .rankLessThan:
            return "figure.racquetball"
        case .rankBetween:
            return "figure.racquetball"
        case .age:
            return "figure.racquetball"
        }
    }
}

enum DataSet: Int, Identifiable {
    case national
    case ligue
    case club
    case favoriteClubs
    case favoritePlayers

    static let allCases: [DataSet] = [.national, .ligue, .club, .favoriteClubs]

    var id: Int { rawValue }
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .national:
            return "National"
        case .ligue:
            return "Ligue"
        case .club:
            return "Club"
        case .favoritePlayers, .favoriteClubs:
            return "Favori"
        }
    }

    var tokens: [SearchToken] {
        var _tokens: [SearchToken] = []
        switch self {
        case .national:
            _tokens = [.club, .ligue, .rankMoreThan, .rankLessThan, .rankBetween]
        case .ligue:
            _tokens = [.club, .rankMoreThan, .rankLessThan, .rankBetween]
        case .club:
            _tokens = [.rankMoreThan, .rankLessThan, .rankBetween]
        case .favoritePlayers, .favoriteClubs:
            _tokens = [.rankMoreThan, .rankLessThan, .rankBetween]
        }

        _tokens.append(.age)
        return _tokens
    }
}

enum SortOption: Int, CaseIterable, Identifiable {
    case name
    case rank
    case tournamentCount
    case points
    case progression

    var id: Int { self.rawValue }
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .name:
            return "Nom"
        case .rank:
            return "Rang"
        case .tournamentCount:
            return "Tournoi"
        case .points:
            return "Points"
        case .progression:
            return "Progression"
        }
    }

    func sortDescriptors(_ ascending: Bool, dataSet: DataSet) -> [SortDescriptor<ImportedPlayer>] {
        switch self {
        case .name:
            return [
                SortDescriptor(\ImportedPlayer.lastName, order: ascending ? .forward : .reverse),
                SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
            ]
        case .rank:
            if dataSet == .national || dataSet == .ligue {
                return [
                    SortDescriptor(\ImportedPlayer.rank, order: ascending ? .forward : .reverse)
                ]
            } else {
                return [
                    SortDescriptor(\ImportedPlayer.rank, order: ascending ? .forward : .reverse),
                    SortDescriptor(\ImportedPlayer.assimilation),
                    SortDescriptor(\ImportedPlayer.lastName),
                ]
            }
        case .tournamentCount:
            return [
                SortDescriptor(
                    \ImportedPlayer.tournamentCount, order: ascending ? .forward : .reverse),
                SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
                SortDescriptor(\ImportedPlayer.lastName),
            ]
        case .points:
            return [
                SortDescriptor(\ImportedPlayer.points, order: ascending ? .forward : .reverse),
                SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
                SortDescriptor(\ImportedPlayer.lastName),
            ]
        case .progression:
            return [
                SortDescriptor(\ImportedPlayer.progression, order: ascending ? .forward : .reverse),
                SortDescriptor(\ImportedPlayer.rank), SortDescriptor(\ImportedPlayer.assimilation),
                SortDescriptor(\ImportedPlayer.lastName),
            ]
        }
    }
}

enum PlayerFilterOption: Int, Hashable, CaseIterable, Identifiable {
    case all = -1
    case male = 1
    case female = 0

    var id: Int { rawValue }

    func icon() -> String {
        switch self {
        case .all:
            return "Tous"
        case .male:
            return "Homme"
        case .female:
            return "Femme"
        }
    }

    var localizedPlayerLabel: String {
        switch self {
        case .female:
            return "joueuse"
        default:
            return "joueur"
        }
    }

}
