//
//  PadelRule.swift
//  Padel Tournament
//
//  Created by razmig on 27/02/2023.
//

import Foundation

enum RankSource: Hashable {
    case national
    case ligue
    case club(assimilation: Bool)
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .national:
            return "Classement National"
        case .ligue:
            return "Classement Ligue"
        case .club:
            return "Classement Club"
        }
    }
}

protocol TournamentBuildHolder: Identifiable {
    var id: String { get }
    var category: TournamentCategory { get }
    var level: TournamentLevel { get }
    var age: FederalTournamentAge { get }
    func buildHolderTitle() -> String
}

struct TournamentBuild: TournamentBuildHolder, Hashable, Codable, Identifiable {
    var id: String { identifier }
    let category: TournamentCategory
    let level: TournamentLevel
    let age: FederalTournamentAge
//    var japIdentifier: Int? = nil
//    var japFirstName: String? = nil
//    var japLastName: String? = nil

    func buildHolderTitle() -> String {
        localizedLabel()
    }
    
    var identifier: String {
        level.localizedLabel()+":"+category.localizedLabel()+":"+age.localizedLabel()
    }
    
    var computedLabel: String {
        if age == .senior { return localizedLabel() }
        return localizedLabel() + " " + localizedAge
    }
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        level.localizedLabel() + category.localizedLabel(.short)
    }
    
    var localizedTitle: String {
        level.localizedLabel() + " " + category.localizedLabel()
    }
    
    var localizedAge: String {
        age.tournamentDescriptionLabel
    }
}

extension TournamentBuild {
    
    init?(category: String, level: String, age: FederalTournamentAge = .senior) {
        guard let levelFound = TournamentLevel.allCases.first(where: { $0.localizedLabel() == level }) else { return nil }
        
        var c = category
        if c.hasPrefix("ME") {
            c = "H"
        }
        
        if c.hasPrefix("F") {
            c = "D"
        }
        
        guard let categoryFound = TournamentCategory.allCases.first(where: { c.canonicalVersion.hasPrefix($0.buildLabel.canonicalVersion) }) else { return nil }
        self.level = levelFound
        self.category = categoryFound
        self.age = age
    }
}

enum FederalTournamentType: String, Hashable, Codable, CaseIterable, Identifiable {
    case tournoi = "P"
    case championnatParEquipe = "S"
    case championnatParPaire = "L"
    
    var id: String { self.rawValue }
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .tournoi:
            return "Tournois"
        case .championnatParEquipe:
            return "Championnats par équipes"
        case .championnatParPaire:
            return "Championnats par paires"
        }
    }
}

enum TournamentDifficulty {
    
    case rankS
    case rankA
    case rankB
    case rankC
    
    init?(gameDifference: Double) {
        switch gameDifference {
        case ..<3:
            self = .rankS
        case ..<5:
            self = .rankA
        case ..<7:
            self = .rankB
        case 7...:
            self = .rankC
        default:
            return nil
        }
    }

    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .rankS:
            return "S"
        case .rankA:
            return "A"
        case .rankB:
            return "B"
        case .rankC:
            return "C"
        }
    }
    
    var backgroundColor: String {
        switch self {
        case .rankS:
            return "#d4af37"
        case .rankA:
            return "#c0c0c0"
        case .rankB:
            return "#cd7f32"
        case .rankC:
            return "#DCC2E0"
        }
    }
}

enum FederalTournamentAge: Int, Hashable, Codable, CaseIterable, Identifiable {
    case unlisted = 0
    case a11_12 = 120
    case a13_14 = 140
    case a15_16 = 160
    case a17_18 = 180
    case senior = 200
    case a45 = 450
    case a55 = 550

    init?(rawValue: Int?) {
        guard let value = rawValue else { return nil }
        self.init(rawValue: value)
    }
    
    static func mostRecent(inTournaments tournaments: [Tournament]) -> Self {
        return tournaments.first?.federalTournamentAge ?? .senior
    }

    static func mostUsed(inTournaments tournaments: [Tournament]) -> Self {
        let countedSet = NSCountedSet(array: tournaments.map { $0.federalTournamentAge })
        let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
        if mostFrequent != nil {
            return mostFrequent as! FederalTournamentAge
        } else {
            return mostRecent(inTournaments: tournaments)
        }
    }
    
    var id: Int { self.rawValue }
    
    var order: Int {
        switch self {
        case .unlisted:
            return 7
        case .a11_12:
            return 6
        case .a13_14:
            return 5
        case .a15_16:
            return 4
        case .a17_18:
            return 3
        case .senior:
            return 0
        case .a45:
            return 1
        case .a55:
            return 2
        }
    }
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .unlisted:
            return displayStyle == .title ? "Aucune" : ""
        case .a11_12:
            return "11/12 ans"
        case .a13_14:
            return "13/14 ans"
        case .a15_16:
            return "15/16 ans"
        case .a17_18:
            return "17/18 ans"
        case .senior:
            return "Senior"
        case .a45:
            return "45 ans"
        case .a55:
            return "55 ans"
        }
    }
    
    var tournamentDescriptionLabel: String {
        switch self {
        case .senior:
            return ""
        case .a45, .a55:
            return "+" + localizedLabel()
        default:
            return localizedLabel()
        }
    }
}

enum TournamentLevel: Int, Hashable, Codable, CaseIterable, Identifiable {
    case unlisted = 0
    case p25 = 25
    case p100 = 100
    case p250 = 250
    case p500 = 500
    case p1000 = 1000
    case p1500 = 1500
    case p2000 = 2000

    init?(rawValue: Int?) {
        guard let value = rawValue else { return nil }
        self.init(rawValue: value)
    }
    
    func hideWeight() -> Bool {
        switch self {
        case .unlisted:
            return true
        default:
            return false
        }
    }
    
    static func mostRecent(inTournaments tournaments: [Tournament]) -> Self {
        return tournaments.first?.tournamentLevel ?? .p100
    }

    static func mostUsed(inTournaments tournaments: [Tournament]) -> Self {
        let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentLevel })
        let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
        if mostFrequent != nil {
            return mostFrequent as! TournamentLevel
        } else {
            return mostRecent(inTournaments: tournaments)
        }
    }

    var id: Int { self.rawValue }
    
    func minimumPlayerRank(category: TournamentCategory, ageCategory: FederalTournamentAge) -> Int {
        switch self {
        case .p25:
            switch ageCategory {
            case .senior, .a45, .a55:
                return category == .men ? 10000 : 1000
            default:
                return 0
            }
        case .p100:
            switch ageCategory {
            case .senior, .a45, .a55:
                return category == .men ? 2000 : 300
            default:
                return 0
            }

        case .p250:
            switch ageCategory {
            case .senior, .a45, .a55:
                return category == .men ? 500 : 100
            default:
                return 0
            }

        default:
            return 0
        }
    }
    
    func federalFormatForGroupStage() -> MatchFormat {
        federalFormatForBracketRound(5)
    }
    
    func federalFormatForBracketRound(_ roundIndex: Int) -> MatchFormat {
        switch self {
        case .p25:
            return .superTie
        case .p100:
            return .nineGamesDecisivePoint
        case .p250:
            if roundIndex == 0 { //finale
                return .twoSetsDecisivePointSuperTie
            } else {
                return .nineGamesDecisivePoint
            }
            
        case .p500:
            if roundIndex == 0 { //finale
                return .twoSetsDecisivePoint
            } else if roundIndex == 1 { //demi-finale
                return .twoSetsDecisivePointSuperTie
            } else {
                return .nineGamesDecisivePoint
            }
        case .p1000, .p1500:
            if roundIndex <= 1 { //demi / finale
                return .twoSetsDecisivePoint
            } else {
                return .twoSetsDecisivePointSuperTie
            }
        default:
            return .twoSetsDecisivePoint
        }
    }

    func decisivePointRequired(ageCategory: FederalTournamentAge) -> Bool {
        switch ageCategory {
        case .a11_12, .a13_14, .a15_16, .a17_18:
            return true
        default:
            return false
        }
    }
    
    func federalFormatForLoserBracketRound(_ roundIndex: Int) -> MatchFormat {
        switch self {
        case .p25:
            return .superTie
        case .p100, .p250, .p500:
            return .nineGamesDecisivePoint
        default:
            if roundIndex <= 1 { //petite finale
                return .twoSetsDecisivePointSuperTie
            } else {
                return .nineGamesDecisivePoint
            }
        }
    }

    
    var defaultTeamSortingType: TeamSortingType {
        switch self {
        case .p25, .p100, .p250:
            return .inscriptionDate
        default:
            return .rank
        }
    }
    
    var order: Int {
        switch self {
        case .unlisted:
            return 7
        case .p25:
            return 6
        case .p100:
            return 5
        case .p250:
            return 4
        case .p500:
            return 3
        case .p1000:
            return 2
        case .p1500:
            return 1
        case .p2000:
            return 0
        }
    }

    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        if self == .unlisted { return displayStyle == .title ? "Animation" : "Anim." }
        return String(describing: self).capitalized
    }
    
    var coachingIsAuthorized: Bool {
        switch self {
        case .p500, .p1000, .p1500, .p2000:
            return true
        default:
            return false
        }
    }
    
    func points(for rank: Int, count: Int) -> Int {
        if self == .unlisted { return 0 }
        let points = points(for: count)
        if rank >= points.count {
            return points.last!
        } else if rank == 0 {
            return Int(self.rawValue)
        } else {
            return points[rank-1]
        }
    }
    
    func allPoints(for count: Int) -> [Int] {
        [Int(self.rawValue)] + points(for: count)
    }
    
    func points(for count: Int) -> [Int] {
        switch self {
        case .unlisted:
            return []
        case .p25:
            switch count {
            case 9...12:
                return [17, 13, 11, 9, 7, 5, 4, 3, 2, 1]
            case 13...16:
                return [18,16,15,14,13,12,11,10,9,7,5,4,3,2, 1]
            case 17...20:
                return [20,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
            case 21...24:
                return [20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
            case 25...28:
                return [21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
            case _ where count > 28:
                return [23,21,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2, 1]
            default:
                return [15, 12, 9, 6, 4, 2, 1]
            }
        case .p100:
            switch count {
            case 9...12:
                return [65,55, 50, 35, 25, 20, 15, 10, 5, 3, 1]
            case 13...16:
                return [70, 60, 55, 45, 40, 35, 30, 25, 21, 18, 15, 10, 5, 3, 1]
            case 17...20:
                return [75,65,60,55,50,45,40,35,30,25,23,20,18,15,12,10,5,3, 1]
            case 21...24:
                return [75,70,65,60,55,50,47,43,40,37,33,30,28,25,23,20,18,15,12,10,5,3, 1]
            case 25...28:
                return [80,75,70,65,60,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,5,3, 1]
            case _ where count > 28:
                return [80,75,72,70,65,63,60,58,55,53,50,48,45,43,40,38,35,33,30,28,25,23,20,18,15,12,10,8,5,3, 1]
            default:
                return [60,50,40,25,10,5]
            }
        case .p250:
            switch count {
            case 9...12:
                return [163,138,125,88,63,50,38,25,13,8,3]
            case 13...16:
                return [175,150,138,113,100,88,75,63,53,45,38,25,13,8,3]
            case 17...20:
                return [188,163,150,138,123,113,100,88,75,63,58,50,45,38,30,25,13,8,3]
            case 21...24:
                return [188,175,163,150,138,125,118,108,100,93,83,75,70,63,58,50,45,38,30,25,13,8,3]
            case 25...28:
                return [200,188,175,163,150,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,13,8,3]
            case _ where count > 28:
                return [200,188,180,175,163,158,150,145,138,133,125,120,113,108,100,95,88,83,75,70,63,58,50,45,38,30,25,20,13,8,3]
            default:
                return [150,125,100,63,25,13,3]
            }
        case .p500:
            switch count {
            case 9...12:
                return [325,275,250,175,125,100,75,50,25,15,5]
            case 13...16:
                return [350,300,275,225,200,175,150,125,105,90,75,50,25,15,5]
            case 17...20:
                return [375,325,300,275,250,225,200,175,150,125,115,100,90,75,60,50,25,15,5]
            case 21...24:
                return [375,350,325,300,275,250,235,215,200,185,165,150,140,125,115,100,80,75,60,50,25,15,5]
            case 25...28:
                return [400,375,350,325,300,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,25,15,5]
            case _ where count > 28:
                return [400,375,360,350,325,315,300,290,275,265,250,240,225,215,200,190,175,165,150,140,125,115,100,80,75,60,50,40,25,15,5]
            default:
                return [300,250,200,125,50,25,5]
            }
        case .p1000:
            switch count {
            case 9...12:
                return [650,550, 500, 350, 250, 200, 150, 100, 50, 30, 10]
            case 13...16:
                return [700, 600, 550, 450, 400, 350, 300, 250, 210, 180, 150, 100, 50, 30, 10]
            case 17...20:
                return [750,650,600,550,500,450,400,350,300,250,230,200,180,150,120,100,50,30, 10]
            case 21...24:
                return [750,700,650,600,550,500,470,430,400,370,330,300,280,250,230,200,180,150,120,100,50,30, 10]
            case 25...28:
                return [800,750,700,650,600,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,50,30, 10]
            case _ where count > 28:
                return [800,750,720,700,650,630,600,580,550,530,500,480,450,430,400,380,350,330,300,280,250,230,200,180,150,120,100,80,50,30, 10]
            default:
                return [600,500,400,250,100,50]
            }
        case .p1500:
            switch count {
            case 21...24:
                return [1125,1050,975,900,825,750,705,645,600,555,495,450,420,375,345,300,270,225,180,150,75,45,15]
            case 25...28:
                return [1200,1125,1050,975,900,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,75,45,15]
            case _ where count > 28:
                return [1200,1125,1080,1050,975,945,900,870,825,795,750,720,675,645,600,570,525,495,450,420,375,345,300,270,225,180,150,120,75,45,15]
            default:
                return [1125,975,900,825,750,675,600,525,450,375,345,300,270,225,180,150,75,45,15]
            }
        case .p2000:
            return [1600,1440,1300,1180,1120,1060,1000,880,840,800,760,720,680,640,600,540,520,500,480,460,440,420,400,260,200,40]
        }
    }
    
    var ranges: [PlayersCountRange] {
        switch self {
        case .p1500:
            return [.N16, .N24, .N28, .N32]
        case .p2000:
            return [.N32]
        default:
            return PlayersCountRange.allCases
        }
    }
    
//    enum NewBallSystem {
//        case perField
//        case perMatch(fromRound: Int?)
//        
//        func localizedLabel(loserBracket: Bool = false) -> String {
//            switch self {
//            case .perField:
//                return "3 / piste"
//            case .perMatch(let fromRound):
//                if fromRound != nil {
//                    if loserBracket {
//                        return "3 / match pour les perdants des \(RoundLabel.shortLabels[fromRound!].lowercased())s"
//                    } else {
//                        return "3 / match à partir des \(RoundLabel.shortLabels[fromRound!].lowercased())s"
//                    }
//                } else {
//                    return "3 / match"
//                }
//            }
//        }
//    }
//
    func minimumFormatFinalTableAndQualifier(roundIndex: Int) -> MatchFormat? {
        switch self {
        case .p25, .unlisted:
            return nil
        case .p100:
            return .nineGamesDecisivePoint
        case .p250:
            if roundIndex == 0 { //final
                return .twoSetsDecisivePointSuperTie
            } else {
                return .nineGamesDecisivePoint
            }
        case .p500:
            if roundIndex == 0 { //final
                return .twoSetsDecisivePoint
            } else if roundIndex == 1 { //demi
                return .twoSetsDecisivePointSuperTie
            } else {
                return .nineGamesDecisivePoint
            }
        case .p1000, .p1500, .p2000:
            if roundIndex == 3 { //16eme
                return .twoSetsDecisivePoint
            } else {
                return .twoSetsDecisivePointSuperTie
            }
        }
    }
    
    func minimumFormatLoserBracket(roundIndex: Int) -> MatchFormat? {
        switch self {
        case .p25, .unlisted:
            return nil
        case .p100, .p250, .p500:
            return .nineGamesDecisivePoint
        case .p1000, .p1500, .p2000:
            if roundIndex == 1 { //demi
                return .twoSetsDecisivePoint
            } else {
                return .nineGamesDecisivePoint
            }
        }
    }

    
//    func newBallsFinalTable() -> NewBallSystem? {
//        switch self {
//        case .p25, .p100:
//            return .perField
//        case .p250:
//            return .perMatch(fromRound: 1) //demi
//        case .p500:
//            return .perMatch(fromRound: 2) //quart
//        case .p1000, .p1500, .p2000:
//            return .perMatch(fromRound: nil)
//        }
//    }
//
//    func newBallsLoserBracket() -> NewBallSystem? {
//        switch self {
//        case .p25, .p100:
//            return nil
//        case .p250:
//            return .perMatch(fromRound: 1) //demi
//        case .p500, .p1000, .p1500, .p2000:
//            return .perMatch(fromRound: 2) //quart
//        }
//    }
//
}

enum TournamentCategory: Int, Hashable, Codable, CaseIterable, Identifiable {
    case men
    case women
    case mix
    case unlisted

    init?(rawValue: Int?) {
        guard let value = rawValue else { return nil }
        self.init(rawValue: value)
    }
    
    func mandatoryPlayerType() -> [Int] {
        switch self {
        case .unlisted:
            return []
        case .mix:
            return [0, 1]
        case .women:
            return [0, 0]
        case .men:
            return [1, 1]
        }
    }

    var localizedPlayerLabel: String {
        switch self {
        case .women:
            return "joueuse"
        default:
            return "joueur"
        }
    }
    
    var showFemaleInMaleAssimilation: Bool {
        switch self {
        case .men:
            return true
        default:
            return false
        }
    }
    
    static func femaleInMaleAssimilationAddition(_ rank: Int) -> Int {
        switch rank {
        case 1...10: return 400
        case 11...30: return 1000
        case 31...60: return 2000
        case 61...100: return 3000
        case 101...200: return 8000
        case 201...500: return 12000
        default:
            return 15000
        }
    }
    
    static func mostRecent(inTournaments tournaments: [Tournament]) -> Self {
        return tournaments.first?.tournamentCategory ?? .men
    }

    static func mostUsed(inTournaments tournaments: [Tournament]) -> Self {
        let countedSet = NSCountedSet(array: tournaments.map { $0.tournamentCategory })
        let mostFrequent = countedSet.max { countedSet.count(for: $0) < countedSet.count(for: $1) }
        if mostFrequent != nil {
            return mostFrequent as! TournamentCategory
        } else {
            return mostRecent(inTournaments: tournaments)
        }
    }
    
    var id: Int { self.rawValue }
    
    var order: Int {
        switch self {
        case .unlisted:
            return 0
        case .men:
            return 1
        case .women:
            return 2
        case .mix:
            return 3
        }
    }
    
    var buildLabel: String {
        switch self {
        case .unlisted:
            return ""
        case .men:
            return "H"
        case .women:
            return "D"
        case .mix:
            return "M"
        }
    }
    
    var requestLabel: String {
        switch self {
        case .unlisted:
            return ""
        case .men:
            return "DM"
        case .women:
            return "DD"
        case .mix:
            return "DX"
        }
    }
    
    var importingRawValue: String {
        switch self {
        case .unlisted:
            return ""
        case .men:
            return "messieurs"
        case .women:
            return "dames"
        case .mix:
            return "mixte"
        }
    }
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .unlisted:
            return displayStyle == .title ? "Aucune" : ""
        case .men:
            return displayStyle != .short ? "Hommes" : "H"
        case .women:
            return displayStyle != .short ? "Dames" : "D"
        case .mix:
            return displayStyle != .short ? "Mixte" : "MX"
        }
    }
    
    var playerFilterOption: PlayerFilterOption {
        switch self {
        case .men, .unlisted:
            return .all
        case .women:
            return .female
        case .mix:
            return .all
        }
    }
}


enum GroupStageOrderingMode: Int, Hashable, Codable, CaseIterable, Identifiable {
    case random
    case snake
    case swiss

    init?(rawValue: Int?) {
        guard let value = rawValue else { return nil }
        self.init(rawValue: value)
    }
    
    var id: Int { self.rawValue }
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .random:
            return "Au hasard"
        case .snake:
            return "En serpentin"
        case .swiss:
            return "Suisse"
        }
    }
    
    var systemImage: String {
        switch self {
        case .random:
            return "dice.fill"
        case .snake:
            return "arrow.triangle.swap"
        case .swiss:
            return "cross.fill"
        }
    }
}

enum TournamentType: Int, Hashable, Codable, CaseIterable, Identifiable {
    case classic
    case doubleBrackets
    
    var id: Int { self.rawValue }
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .classic:
            return "Classique"
        case .doubleBrackets:
            return "Double Poules"
        }
    }
}

enum TeamPosition: Int, Identifiable, Hashable, Codable, CaseIterable {
    case one
    case two
    
    var id: Int { self.rawValue }
    
    var otherTeam: TeamPosition {
        switch self {
        case .one:
            return .two
        case .two:
            return .one
        }
    }
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        var shortName: String {
            switch self {
            case .one:
                return "#1"
            case .two:
                return "#2"
            }
        }
        
        switch displayStyle {
        case .wide, .title:
            return "Équipe " + shortName
        case .short:
            return shortName
        }
    }
}

enum SetFormat: Int, Hashable, Codable {
    case nine
    case four
    case six
    case superTieBreak
    case megaTieBreak
    
    func shouldTiebreak(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool {
        if let tieBreak {
            return (scoreTeamOne + scoreTeamTwo) >= (2 * tieBreak) && (scoreTeamOne == tieBreak || scoreTeamTwo == tieBreak)
        } else {
            return false
        }
    }
    
    func winner(teamOne: Int, teamTwo: Int) -> TeamPosition {
        return teamOne >= teamTwo ? .one : .two
    }
    
    func hasEnded(teamOne: Int, teamTwo: Int) -> Bool {
        switch self {
        case .nine:
            if teamOne == 9 || teamTwo == 9 {
                return true
            }
        case .four:
            if teamOne == 5 || teamTwo == 5 {
                return true
            }
        case .six:
            if teamOne == 7 || teamTwo == 7 {
                return true
            }
        case .superTieBreak, .megaTieBreak:
            if teamOne == 1 || teamTwo == 1 {
                return true
            }
        }
        return (teamOne >= scoreToWin && teamOne >= teamTwo + 2) || (teamTwo >= scoreToWin && teamTwo >= teamOne + 2)
    }
    
    var tieBreak: Int? {
        switch self {
        case .nine:
            return 8
        case .four:
            return 4
        case .six:
            return 6
        case .superTieBreak, .megaTieBreak:
            return nil
        }
    }
    
    func disableValuesForTeamTwo(with teamOneScore: Int) -> [Int] {
        switch self {
        case .nine:
            if teamOneScore == 9 {
                return [9]
            }
        case .four:
            if teamOneScore == 4 {
                return []
            }
        case .six:
            if teamOneScore == 6 {
                return []
            }
        case .superTieBreak:
            if teamOneScore == 10 {
                return []
            }
        case .megaTieBreak:
            if teamOneScore == 15 {
                return []
            }
        }
        return []
    }
    
    var possibleValues: [Int] {
        switch self {
        case .nine:
            return [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        case .four:
            return [5, 4, 3, 2, 1, 0]
        case .six:
            return [7, 6, 5, 4, 3, 2, 1, 0]
        case .superTieBreak:
            return [10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        case .megaTieBreak:
            return [15, 14, 13, 12, 11, 10,9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
        }
    }
    
    var scoreToWin: Int {
        switch self {
        case .nine:
            return 9
        case .four:
            return 4
        case .six:
            return 6
        case .superTieBreak:
            return 10
        case .megaTieBreak:
            return 15
        }
    }

    var firstGameFormat: Format {
        switch self {
        case .megaTieBreak:
            return .tiebreakFifteen
        case .superTieBreak:
            return .tiebreakTen
        default:
            return .normal
        }
    }
}

enum MatchType: String {
    case bracket = "bracket"
    case groupStage = "groupStage"
    case loserBracket = "loserBracket"
}

enum MatchFormat: Int, Hashable, Codable, CaseIterable {
    case twoSets
    case twoSetsSuperTie
    case twoSetsOfFourGames
    case nineGames
    case superTie
    case megaTie

    case twoSetsDecisivePoint
    case twoSetsDecisivePointSuperTie
    case twoSetsOfFourGamesDecisivePoint
    case nineGamesDecisivePoint
    
    init?(rawValue: Int?) {
        guard let value = rawValue else { return nil }
        self.init(rawValue: value)
    }
    
    func defaultWalkOutScore(_ asWalkOutTeam: Bool) -> [Int] {
        Array(repeating: asWalkOutTeam ? 0 : setFormat.scoreToWin, count: setsToWin)
    }

    var weight: Int {
        switch self {
        case .twoSets, .twoSetsDecisivePoint:
            return 0
        case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
            return 1
        case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
            return 2
        case .nineGames, .nineGamesDecisivePoint:
            return 3
        case .superTie:
            return 4
        case .megaTie:
            return 5
        }
    }
    
    var rank: Int {
        switch self {
        case .twoSets:
            return 0
        case .twoSetsDecisivePoint:
            return 0
        case .twoSetsSuperTie:
            return 1
        case .twoSetsDecisivePointSuperTie:
            return 1
        case .twoSetsOfFourGames:
            return 2
        case .twoSetsOfFourGamesDecisivePoint:
            return 2
        case .nineGames:
            return 3
        case .nineGamesDecisivePoint:
            return 3
        case .superTie:
            return 4
        case .megaTie:
            return 5
        }
    }
    
    static func defaultFormatForMatchType(_ matchType: MatchType) -> MatchFormat {
        switch matchType {
        case .bracket:
            DataStore.shared.user.bracketMatchFormatPreference ?? .nineGamesDecisivePoint
        case .groupStage:
            DataStore.shared.user.groupStageMatchFormatPreference ?? .nineGamesDecisivePoint
        case .loserBracket:
            DataStore.shared.user.loserBracketMatchFormatPreference ?? .nineGamesDecisivePoint
        }
    }
    
    static var allCases: [MatchFormat] {
        [.twoSets, .twoSetsDecisivePoint, .twoSetsSuperTie, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie]
    }
    
    func winner(scoreTeamOne: Int, scoreTeamTwo: Int) -> TeamPosition {
        scoreTeamOne >= scoreTeamTwo ? .one : .two
    }
    
    func hasEnded(scoreTeamOne: Int, scoreTeamTwo: Int) -> Bool {
        scoreTeamOne == setsToWin || scoreTeamTwo == setsToWin
    }
    
    var canSuperTie: Bool {
        switch self {
        case .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie:
            return true
        default:
            return false
        }
    }
    
    func getEstimatedDuration(_ additionalDuration: Int = 0) -> Int {
        estimatedDuration + additionalDuration
    }
    
    private var estimatedDuration: Int {
        DataStore.shared.user.matchFormatsDefaultDuration?[self] ?? defaultEstimatedDuration
    }
    
    func formattedEstimatedDuration(_ additionalDuration: Int = 0) -> String {
        Duration.seconds((estimatedDuration + additionalDuration) * 60).formatted(.units(allowed: [.minutes]))
    }

    func formattedEstimatedBreakDuration() -> String {
        var label = Duration.seconds(breakTime.breakTime * 60).formatted(.units(allowed: [.minutes]))
        if breakTime.matchCount > 1 {
            label += " après \(breakTime.matchCount) match"
            label += breakTime.matchCount.pluralSuffix
        }
        return label
    }

    var defaultEstimatedDuration: Int {
        switch self {
        case .twoSets:
            return 105
        case .twoSetsDecisivePoint:
            return 90
        case .twoSetsSuperTie:
            return 80
        case .twoSetsDecisivePointSuperTie:
            return 70
        case .twoSetsOfFourGames:
            return 60
        case .twoSetsOfFourGamesDecisivePoint:
            return 50
        case .nineGames:
            return 45
        case .nineGamesDecisivePoint:
            return 40
        case .megaTie:
            return 30
        case .superTie:
            return 25
        }
    }

    
    var estimatedTimeWithBreak: Int {
        estimatedDuration + breakTime.breakTime
    }
    
    var breakTime: (breakTime: Int, matchCount: Int) {
        switch self {
        case .twoSets, .twoSetsDecisivePoint:
            return (90, 1)
        case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
            return (60, 1)
        case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint:
            return (30, 1)
        case .superTie:
            return (15, 3)
        case .megaTie:
            return (5, 1)
        }
    }
    
    func maximumMatchPerDay(for matchCount: Int) -> Int {
        switch self {
        case .twoSets, .twoSetsDecisivePoint:
            return matchCount < 5 ? 2 : 0
        case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
            return matchCount < 6 ? 3 : 0
        case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint, .nineGames, .nineGamesDecisivePoint:
            return matchCount < 7 ? 6 : 2
        case .superTie:
            return 7
        case .megaTie:
            return 7
        }
    }
    
    var hasDecisivePoint: Bool {
        switch self {
        case .nineGamesDecisivePoint, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie:
            return true
        default:
            return false
        }
    }
    
    func newSetFormat(setCount: Int) -> SetFormat {
        if setCount == 2 && canSuperTie {
            return .superTieBreak
        }
        return setFormat
    }
    
    var suffix: String {
        switch self {
        case .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie, .twoSetsOfFourGamesDecisivePoint, .nineGamesDecisivePoint:
            return " [Point Décisif]"
        default:
            return ""
        }
    }
    
    var longPrefix: String {
        return "Format \(format) : "
    }

    var shortPrefix: String {
        return "\(format) : "
    }

    var format: String {
        switch self {
        case .twoSets:
            return "A1"
        case .twoSetsSuperTie:
            return "B1"
        case .twoSetsOfFourGames:
            return "C1"
        case .nineGames:
            return "D1"
        case .superTie:
            return "E"
        case .megaTie:
            return "F"
        case .twoSetsDecisivePoint:
            return "A2"
        case .twoSetsDecisivePointSuperTie:
            return "B2"
        case .twoSetsOfFourGamesDecisivePoint:
            return "C2"
        case .nineGamesDecisivePoint:
            return "D2"
        }
    }
    
    var longLabel: String {
        switch self {
        case .twoSets, .twoSetsDecisivePoint:
            return "2 sets de 6"
        case .twoSetsSuperTie, .twoSetsDecisivePointSuperTie:
            return "2 sets de 6, supertie au 3ème"
        case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
            return "2 sets de 4, tiebreak à 4/4, supertie au 3ème"
        case .nineGames, .nineGamesDecisivePoint:
            return "9 jeux, tiebreak à 8/8"
        case .superTie:
            return "supertie de 10 points"
        case .megaTie:
            return "supertie de 15 points"
        }
    }
    
    var computedShortLabelWithoutPrefix: String {
        longLabel + suffix
    }

    var computedShortLabel: String {
        shortPrefix + longLabel + suffix
    }
    
    var computedLongLabel: String {
        longPrefix + longLabel + suffix
    }
    
    var setsToWin: Int {
        switch self {
        case .twoSets, .twoSetsSuperTie, .twoSetsOfFourGames, .twoSetsDecisivePoint, .twoSetsOfFourGamesDecisivePoint, .twoSetsDecisivePointSuperTie:
            return 2
        case .nineGames, .nineGamesDecisivePoint, .superTie, .megaTie:
            return 1
        }
    }
    
    var setFormat: SetFormat {
        switch self {
        case .twoSets, .twoSetsSuperTie, .twoSetsDecisivePoint, .twoSetsDecisivePointSuperTie:
            return .six
        case .twoSetsOfFourGames, .twoSetsOfFourGamesDecisivePoint:
            return .four
        case .nineGames, .nineGamesDecisivePoint:
            return .nine
        case .superTie:
            return .superTieBreak
        case .megaTie:
            return .megaTieBreak
        }
    }
}

enum Format: Int, Hashable, Codable {
    case normal
    case tiebreakSeven
    case tiebreakTen
    case tiebreakFifteen
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .normal:
            return "normal"
        case .tiebreakSeven:
            return "tie-break en 7"
        case .tiebreakTen:
            return "tie-break en 10"
        case .tiebreakFifteen:
            return "tie-break en 15"
        }
    }
    var isTiebreak: Bool {
        switch self {
        case .normal:
            return false
        case .tiebreakSeven, .tiebreakTen, .tiebreakFifteen:
            return true
        }
    }
    
    var scoreToWin: Int? {
        switch self {
        case .normal:
            return nil
        case .tiebreakSeven:
            return 7
        case .tiebreakTen:
            return 10
        case .tiebreakFifteen:
            return 15
        }
    }
}

enum ActionType: Int, Identifiable {
    case fault
    case winner
    
    var id: Self {
        self
    }
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .fault:
            return "Faute"
        case .winner:
            return "Point"
        }
    }
    /*
     Break points won
     Break points
     Errors
     Smash winners
     Smashes
     Winners
     Volley winners
     
     Total points won
     % points won
     Total break points
     Break points won
     Gold points won
     Gold points won returning
     Gold points won with service
     Most consecutive points won
     Total set points
     Set points won
     Match points
     */
}

enum EventType: Int, CaseIterable, Identifiable {
    case approvedTournament
    case friendlyTournament
    case simulation
    case animation

    var id: Self {
        self
    }
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .approvedTournament:
            return "Tournoi homologué"
        case .friendlyTournament:
            return "Tournoi amical"
        case .simulation:
            return "Simulation"
        case .animation:
            return "Animation"
        }
    }
}

enum TeamSortingType: Int, Identifiable, CaseIterable, Hashable, Codable {
    case rank = 1
    case inscriptionDate = 2
    
    var id: Int { rawValue }
    
    init?(rawValue: Int?) {
        guard let value = rawValue else { return nil }
        self.init(rawValue: value)
    }
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .rank:
            return "Rang"
        case .inscriptionDate:
            return "Date d'inscription"
        }
    }
}

enum PlayersCountRange: Int, CaseIterable {
    case N8 = 8
    case N12 = 12
    case N16 = 16
    case N20 = 20
    case N24 = 24
    case N28 = 28
    case N32 = 32
    
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .N8:
            return "4 à 8"
        case .N12:
            return "9 à 12"
        case .N16:
            return "13 à 16"
        case .N20:
            return "17 à 20"
        case .N24:
            return "21 à 24"
        case .N28:
            return "25 à 28"
        case .N32:
            return "29 à 32"
        }
    }
}

enum RoundRule {
    static func loserBrackets(index: Int) -> [String] {
        switch index {
        case 1:
            return ["#3/#4"]
        case 2:
            return ["#5/#6", "#7/#8"]
        default:
            return ["#9/#10", "#11/#12", "#13/#14", "#15/#16", "#17/#18", "#19/#20", "#21/#22", "#23/#24", "#25/#26", "#27/#28", "#29/#30", "#31/#32"]
        }
    }
    
    static func teamsInFirstRound(forTeams teams: Int) -> Int {
        Int(pow(2.0, ceil(log2(Double(teams)))))
    }
    
    static func numberOfMatches(forTeams teams: Int) -> Int {
        teamsInFirstRound(forTeams: teams) - 1
    }
    
    static func numberOfRounds(forTeams teams: Int) -> Int {
        Int(log2(Double(teamsInFirstRound(forTeams: teams))))
    }
    
    static func matchIndex(fromRoundIndex roundIndex: Int) -> Int {
        guard roundIndex >= 0 else {
            return -1 // Invalid round index
        }

        return (1 << roundIndex) - 1
    }
    
    static func matchIndex(fromBracketPosition: Int) -> Int {
        roundIndex(fromMatchIndex: fromBracketPosition / 2) + fromBracketPosition%2
    }
    
    static func roundIndex(fromMatchIndex matchIndex: Int) -> Int {
        Int(log2(Double(matchIndex + 1)))
    }
    
    static func numberOfMatches(forRoundIndex roundIndex: Int) -> Int {
        Int(pow(2.0, Double(roundIndex)))
    }
    
    static func matchIndexWithinRound(fromMatchIndex matchIndex: Int) -> Int {
        let roundIndex = roundIndex(fromMatchIndex: matchIndex)
        let matchIndexWithinRound = matchIndex - (Int(pow(2.0, Double(roundIndex))) - 1)
        return matchIndexWithinRound
    }
    
    static func roundName(fromMatchIndex matchIndex: Int) -> String {
        let roundIndex = roundIndex(fromMatchIndex: matchIndex)
        return roundName(fromRoundIndex: roundIndex)
    }
    
    static func roundName(fromRoundIndex roundIndex: Int, displayStyle: DisplayStyle = .wide) -> String {
        switch roundIndex {
        case 0:
            return "Finale"
        case 1:
            if displayStyle == .short {
                return "Demi"
            }
            return "Demi-finale"
        case 2:
            if displayStyle == .short {
                return "Quart"
            }
            return "Quart de finale"
        default:
            return "\(Int(pow(2.0, Double(roundIndex))))ème"
        }
    }
}

enum AnimationType: Int, CaseIterable, Hashable, Identifiable {
    case playerAnimation
    case upAndDown
    case brawl
    
    var id: Int { rawValue }
    func localizedLabel(_ displayStyle: DisplayStyle = .wide) -> String {
        switch self {
        case .playerAnimation:
            return "Par joueur"
        case .upAndDown:
            return "Montante / Descendante"
        case .brawl:
            return "Brawl"
        }
    }
    
    var descriptionLabel: String {
        switch self {
        case .playerAnimation:
            return "Chaque joueur joue avec quelqu'un de différent à chaque rotation (8 à 12 joueurs)"
        case .upAndDown:
            return "Les gagnants montent sur le terrain d'à côté, les perdants descendent"
        case .brawl:
            return "A chaque rotation, les gagnants de la rotation précédente se jouent entre eux"
        }
    }
}

