//
//  TableStructureView.swift
//  Padel Tournament
//
//  Created by Razmig Sarkissian on 04/10/2023.
//

import SwiftUI
import LeStorage

struct TableStructureView: View {
    @Environment(Tournament.self) private var tournament: Tournament
    @EnvironmentObject private var dataStore: DataStore
    @Environment(\.dismiss) var dismiss
    @State private var presentRefreshStructureWarning: Bool = false
    @State private var teamCount: Int = 0
    @State private var groupStageCount: Int = 0
    @State private var teamsPerGroupStage: Int = 0
    @State private var qualifiedPerGroupStage: Int = 0
    @State private var groupStageAdditionalQualified: Int = 0
    @State private var updatedElements: Set<StructureElement> = Set()
    @State private var structurePreset: PadelTournamentStructurePreset = .manual
    @State private var buildWildcards: Bool = true
    @FocusState private var stepperFieldIsFocused: Bool
    
    func displayWarning() -> Bool {
        let unsortedTeamsCount = tournament.unsortedTeamsCount()
        return tournament.shouldWarnOnlineRegistrationUpdates() && teamCount != tournament.teamCount && (tournament.teamCount <= unsortedTeamsCount || teamCount <= unsortedTeamsCount)
    }
    
    var qualifiedFromGroupStage: Int {
        groupStageCount * qualifiedPerGroupStage
    }
    
    var teamsFromGroupStages: Int {
        groupStageCount * teamsPerGroupStage
    }
    
    var maxMoreQualified: Int {
        if teamsPerGroupStage - qualifiedPerGroupStage > 1 {
            return groupStageCount
        } else if teamsPerGroupStage - qualifiedPerGroupStage == 1 {
            return groupStageCount - 1
        } else {
            return 0
        }
    }
    
    var moreQualifiedLabel: String {
        if groupStageAdditionalQualified == 0 { return "Aucun" }
        return (groupStageAdditionalQualified > 1 ? "les \(groupStageAdditionalQualified)" : "le") + " " + (qualifiedPerGroupStage + 1).ordinalFormatted()
    }
    
    var maxGroupStages: Int {
        teamCount / max(1, teamsPerGroupStage)
    }
    
    var tsPure: Int {
        max(teamCount - groupStageCount * teamsPerGroupStage, 0)
    }


    @ViewBuilder
    var body: some View {
        List {
            if displayWarning() {
                Text("Attention, l'inscription en ligne est activée et vous avez des équipes inscrites en ligne, en modifiant la structure ces équipes seront intégrées ou retirées de votre sélection d'équipes. Padel Club saura prévenir les équipes inscrites en ligne automatiquement.")
                    .foregroundStyle(.logoRed)
            }

            if tournament.state() != .build {
                Section {
                    Picker(selection: $structurePreset) {
                        ForEach(PadelTournamentStructurePreset.allCases) { preset in
                            Text(preset.localizedStructurePresetTitle()).tag(preset)
                        }
                    } label: {
                        Text("Préréglage")
                    }
                } footer: {
                    Text(structurePreset.localizedDescriptionStructurePresetTitle())
                }
                .onChange(of: structurePreset) {
                    _updatePreset()
                }
            }
            
            Section {
                LabeledContent {
                    StepperView(count: $teamCount, minimum: 4, maximum: 128) {

                    } submitFollowUpAction: {
                        _verifyValueIntegrity()
                    }
                } label: {
                    Text("Nombre d'équipes")
                }
                LabeledContent {
                    StepperView(count: $groupStageCount, minimum: 0, maximum: maxGroupStages) {
                        
                    } submitFollowUpAction: {
                        _verifyValueIntegrity()
                    }
                } label: {
                    Text("Nombre de poules")
                }
            } footer: {
                Text("Vous pourrez modifier la taille de vos poules de manière spécifique dans l'écran des poules.")
            }
            
            if groupStageCount > 0 {
                if (teamCount / groupStageCount) > 1 {
                    Section {
                        LabeledContent {
                            StepperView(count: $teamsPerGroupStage, minimum: 2, maximum: (teamCount / groupStageCount)) {
                                
                            } submitFollowUpAction: {
                                _verifyValueIntegrity()
                            }
                        } label: {
                            Text("Équipes par poule")
                        }
                        
                        if structurePreset != .doubleGroupStage {
                            LabeledContent {
                                StepperView(count: $qualifiedPerGroupStage, minimum: 0, maximum: (teamsPerGroupStage-1)) {
                                    
                                } submitFollowUpAction: {
                                    _verifyValueIntegrity()
                                }
                            } label: {
                                Text("Qualifié\(qualifiedPerGroupStage.pluralSuffix) par poule")
                            }
                            
                            if qualifiedPerGroupStage < teamsPerGroupStage - 1 {
                                LabeledContent {
                                    StepperView(count: $groupStageAdditionalQualified, minimum: 0, maximum: maxMoreQualified) {
                                        
                                    } submitFollowUpAction: {
                                        _verifyValueIntegrity()
                                    }
                                } label: {
                                    Text("Qualifié\(groupStageAdditionalQualified.pluralSuffix) supplémentaires")
                                    Text(moreQualifiedLabel)
                                }
                                .onChange(of: groupStageAdditionalQualified) {
                                    if groupStageAdditionalQualified == groupStageCount {
                                        qualifiedPerGroupStage += 1
                                        groupStageAdditionalQualified -= groupStageCount
                                    }
                                }
                            }
                        }
                        
                        if groupStageCount > 0 && teamsPerGroupStage > 0 {
                            if structurePreset != .doubleGroupStage {
                                LabeledContent {
                                    let mp = teamsPerGroupStage * (teamsPerGroupStage - 1) / 2
                                    Text(mp.formatted())
                                } label: {
                                    Text("Matchs à jouer par poule")
                                }
                            } else {
                                LabeledContent {
                                    let mp = teamsPerGroupStage * (teamsPerGroupStage - 1) / 2
                                    Text(mp.formatted())
                                } label: {
                                    Text("Matchs à jouer par poule")
                                    Text("Première phase")
                                }

                                LabeledContent {
                                    let mp = (groupStageCount * (groupStageCount - 1) / 2)
                                    Text(mp.formatted())
                                } label: {
                                    Text("Matchs à jouer par poule")
                                    Text("Deuxième phase")
                                }
                                
                                LabeledContent {
                                    let mp = groupStageCount - 1 + teamsPerGroupStage - 1
                                    Text(mp.formatted())
                                } label: {
                                    Text("Matchs à jouer par équipe")
                                    Text("Total")
                                }

                            }
                        }
                    }
                } else {
                    ContentUnavailableView("Erreur", systemImage: "divide.circle.fill", description: Text("Il y n'y a pas assez d'équipe pour ce nombre de poule."))
                }
            }
            
            Section {
                let tf = max(teamCount - teamsFromGroupStages + qualifiedFromGroupStage + (groupStageCount > 0 ? groupStageAdditionalQualified : 0), 0)
                if groupStageCount > 0 {
                    if structurePreset != .doubleGroupStage {
                        LabeledContent {
                            Text(teamsFromGroupStages.formatted())
                        } label: {
                            Text("Équipes en poule")
                        }

                        
                        LabeledContent {
                            Text((qualifiedFromGroupStage + (groupStageCount > 0 ? groupStageAdditionalQualified : 0)).formatted())
                        } label: {
                            Text("Équipes qualifiées de poule")
                        }
                    }
                }
                
                if structurePreset != .doubleGroupStage {
                    LabeledContent {
                        Text(tsPure.formatted())
                    } label: {
                        Text("Nombre de têtes de série")
                        
                        if groupStageCount > 0 && tsPure > 0 && (tsPure > teamCount / 2 || tsPure < teamCount / 8 || tsPure < qualifiedFromGroupStage + groupStageAdditionalQualified) {
                            Text("Attention !").foregroundStyle(.red)
                        }
                    }
                    LabeledContent {
                        Text(tf.formatted())
                    } label: {
                        Text("Équipes en tableau final")
                    }
                } else {
                    LabeledContent {
                        let mp1 = teamsPerGroupStage * (teamsPerGroupStage - 1) / 2 * groupStageCount
                        let mp2 = (groupStageCount * (groupStageCount - 1) / 2) * teamsPerGroupStage
                        Text((mp1 + mp2).formatted())
                    } label: {
                        Text("Total de matchs")
                    }
                }
            } footer: {
                if tsPure > 0 && structurePreset != .doubleGroupStage, groupStageCount > 0 {
                    if tsPure > teamCount / 2 {
                        Text("Le nombre de têtes de série ne devrait pas être supérieur à la moitié de l'effectif.").foregroundStyle(.red)
                    } else if tsPure < teamCount / 8 {
                        Text("À partir du moment où vous avez des têtes de série, leur nombre ne devrait pas être inférieur à 1/8ème de l'effectif.").foregroundStyle(.red)
                    } else if tsPure < qualifiedFromGroupStage + groupStageAdditionalQualified {
                        Text("Le nombre de têtes de série ne devrait pas être inférieur au nombre de paires qualifiées sortantes.").foregroundStyle(.red)
                    }
                }
            }
            
            if structurePreset.hasWildcards() && tournament.level.wildcardArePossible() {
                Section {
                    Toggle("Avec wildcards", isOn: $buildWildcards)
                } footer: {
                    Text("Padel Club réservera des places pour eux dans votre liste d'inscription.")
                }
            }
            
            if tournament.rounds().isEmpty, tournament.state() == .build {
                Section {
                    RowButtonView("Ajouter un tableau", role: .destructive) {
                        tournament.buildBracket(minimalBracketTeamCount: 4)
                    }
                } footer: {
                    Text("Vous pourrez ensuite modifier le nombre de tour dans l'écran de réglages du tableau.")
                }
            }
            
            
            if tournament.state() != .initial {
                Section {
                    RowButtonView("Sauver sans reconstuire l'existant") {
                        _saveWithoutRebuild()
                    }
                }
                
                Section {
                    RowButtonView("Reconstruire les poules", role:.destructive) {
                        _save(rebuildEverything: false)
                    }
                }
                
                Section {
                    RowButtonView("Tout refaire", role: .destructive) {
                        _save(rebuildEverything: true)
                    }
                }
                
                Section {
                    RowButtonView("Remise-à-zéro", role: .destructive) {
                        tournament.removeWildCards()
                        tournament.deleteGroupStages()
                        tournament.deleteStructure()
                        
                        if structurePreset != .manual {
                            structurePreset = PadelTournamentStructurePreset.manual
                        } else {
                            _updatePreset()
                        }
                        
                        tournament.teamCount = teamCount
                        tournament.groupStageCount = groupStageCount
                        tournament.teamsPerGroupStage = teamsPerGroupStage
                        tournament.qualifiedPerGroupStage = qualifiedPerGroupStage
                        tournament.groupStageAdditionalQualified = groupStageAdditionalQualified
                        
                        do {
                            try dataStore.tournaments.addOrUpdate(instance: tournament)
                        } catch {
                            Logger.error(error)
                        }
                    }
                }
            }
        }
        .focused($stepperFieldIsFocused)
        .onChange(of: stepperFieldIsFocused) {
            if stepperFieldIsFocused {
                DispatchQueue.main.async {
                    UIApplication.shared.sendAction(#selector(UIResponder.selectAll(_:)), to: nil, from: nil, for: nil)
                }
            }
        }
        .toolbarBackground(.visible, for: .navigationBar)
        .onAppear {
            teamCount = tournament.teamCount
            groupStageCount = tournament.groupStageCount
            teamsPerGroupStage = tournament.teamsPerGroupStage
            qualifiedPerGroupStage = tournament.qualifiedPerGroupStage
            groupStageAdditionalQualified = tournament.groupStageAdditionalQualified
        }
        .onChange(of: teamCount) {
            if teamCount != tournament.teamCount {
                updatedElements.insert(.teamCount)
            } else {
                updatedElements.remove(.teamCount)
            }
        }
        .onChange(of: groupStageCount) {
            if groupStageCount != tournament.groupStageCount {
                updatedElements.insert(.groupStageCount)
            } else {
                updatedElements.remove(.groupStageCount)
            }
            
            if structurePreset.isFederalPreset(), groupStageCount == 0 {
                teamCount = structurePreset.tableDimension()
            }
        }
        .onChange(of: teamsPerGroupStage) {
            if teamsPerGroupStage != tournament.teamsPerGroupStage {
                updatedElements.insert(.teamsPerGroupStage)
            } else {
                updatedElements.remove(.teamsPerGroupStage)
            }        }
        .onChange(of: qualifiedPerGroupStage) {
            if qualifiedPerGroupStage != tournament.qualifiedPerGroupStage {
                updatedElements.insert(.qualifiedPerGroupStage)
            } else {
                updatedElements.remove(.qualifiedPerGroupStage)
            }        }
        .onChange(of: groupStageAdditionalQualified) {
            if groupStageAdditionalQualified != tournament.groupStageAdditionalQualified {
                updatedElements.insert(.groupStageAdditionalQualified)
            } else {
                updatedElements.remove(.groupStageAdditionalQualified)
            }
        }
        .toolbar {
            ToolbarItem(placement: .confirmationAction) {
                if tournament.state() == .initial {
                    ButtonValidateView {
                        _save(rebuildEverything: true)
                    }
                } else {
                    let requirements = Set(updatedElements.compactMap { $0.requiresRebuilding })
                    ButtonValidateView(role: .destructive) {
                        if requirements.isEmpty {
                            _save(rebuildEverything: false)
                        } else {
                            presentRefreshStructureWarning = true
                        }
                    }
                    .confirmationDialog("Refaire la structure", isPresented: $presentRefreshStructureWarning, actions: {
                        Button("Sauver sans reconstuire l'existant") {
                            _saveWithoutRebuild()
                        }
                        
                        Button("Reconstruire les poules") {
                            _save(rebuildEverything: false)
                        }
                        
                        Button("Tout refaire", role: .destructive) {
                            _save(rebuildEverything: true)
                        }
                    }, message: {
                        ForEach(Array(requirements)) { requirement in
                            Text(requirement.rebuildingRequirementMessage)
                        }
                    })
                }
            }
        }
        .navigationTitle("Structure")
        .navigationBarTitleDisplayMode(.inline)
    }

    
    private func _saveWithoutRebuild() {
        tournament.teamCount = teamCount
        tournament.groupStageCount = groupStageCount
        let updateGroupStageState = teamsPerGroupStage > tournament.teamsPerGroupStage
        tournament.teamsPerGroupStage = teamsPerGroupStage
        tournament.qualifiedPerGroupStage = qualifiedPerGroupStage
        tournament.groupStageAdditionalQualified = groupStageAdditionalQualified
        if updateGroupStageState {
            tournament.groupStages().forEach { groupStage in
                groupStage.updateGroupStageState()
            }
        }
        
        _checkGroupStagesTeams()
        
        do {
            try dataStore.tournaments.addOrUpdate(instance: tournament)
        } catch {
            Logger.error(error)
        }
        dismiss()
    }
    
    private func _checkGroupStagesTeams() {
        if groupStageCount == 0 {
            let teams = tournament.unsortedTeams().filter({ $0.inGroupStage() })
            teams.forEach { team in
                team.groupStagePosition = nil
                team.groupStage = nil
            }
            do {
                try tournament.tournamentStore?.teamRegistrations.addOrUpdate(contentOfs: teams)
            } catch {
                Logger.error(error)
            }
        }
    }
        
    private func _save(rebuildEverything: Bool = false) {
        _verifyValueIntegrity()
        
        do {
            let requirements = Set(updatedElements.compactMap { $0.requiresRebuilding })

            tournament.teamCount = teamCount
            tournament.groupStageCount = groupStageCount
            tournament.teamsPerGroupStage = teamsPerGroupStage
            tournament.qualifiedPerGroupStage = qualifiedPerGroupStage
            tournament.groupStageAdditionalQualified = groupStageAdditionalQualified

            if rebuildEverything {
                tournament.removeWildCards()
                if structurePreset.hasWildcards(), buildWildcards {
                    tournament.addWildCardIfNeeded(structurePreset.wildcardBrackets(), .bracket)
                    tournament.addWildCardIfNeeded(structurePreset.wildcardQualifiers(), .groupStage)
                }
                tournament.deleteAndBuildEverything(preset: structurePreset)
            } else if (rebuildEverything == false && requirements.contains(.groupStage)) {
                tournament.deleteGroupStages()
                tournament.buildGroupStages()
            }
            
            _checkGroupStagesTeams()

            try dataStore.tournaments.addOrUpdate(instance: tournament)

            dismiss()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
    
    private func _updatePreset() {
        teamCount = structurePreset.tableDimension() + structurePreset.teamsInQualifiers() - structurePreset.qualifiedPerGroupStage() * structurePreset.groupStageCount()
        groupStageCount = structurePreset.groupStageCount()
        teamsPerGroupStage = structurePreset.teamsPerGroupStage()
        qualifiedPerGroupStage = structurePreset.qualifiedPerGroupStage()
        groupStageAdditionalQualified = 0
        buildWildcards = tournament.level.wildcardArePossible()
    }
    
    private func _verifyValueIntegrity() {
        if teamCount > 128 {
            teamCount = 128
        }
        
        if groupStageCount > maxGroupStages {
            groupStageCount = maxGroupStages
        }
        
        if teamCount < 4 {
            teamCount = 4
        }
        
        if groupStageCount < 0 {
            groupStageCount = 0
        }

        if groupStageCount > 0 {
            if teamsPerGroupStage > (teamCount / groupStageCount) {
                teamsPerGroupStage = (teamCount / groupStageCount)
            }

            if qualifiedPerGroupStage > (teamsPerGroupStage-1) {
                qualifiedPerGroupStage = (teamsPerGroupStage-1)
            }
            
            if groupStageAdditionalQualified > maxMoreQualified {
                groupStageAdditionalQualified = maxMoreQualified
            }

            if teamsPerGroupStage < 2 {
                teamsPerGroupStage = 2
            }
            
            if qualifiedPerGroupStage < 0 {
                qualifiedPerGroupStage = 0
            }
            
            if groupStageAdditionalQualified < 0 {
                groupStageAdditionalQualified = 0
            }
        }

    }
}

extension TableStructureView {
    
    enum StructureElement: Int, Identifiable {
        case teamCount
        case groupStageCount
        case teamsPerGroupStage
        case qualifiedPerGroupStage
        case groupStageAdditionalQualified
        var id: Int { self.rawValue }
        
        var requiresRebuilding: RebuildingRequirement? {
            switch self {
            case .teamCount:
                return .all
            case .groupStageCount:
                return .groupStage
            case .teamsPerGroupStage:
                return .groupStage
            case .qualifiedPerGroupStage:
                return nil
            case .groupStageAdditionalQualified:
                return nil
            }
        }
    }
    
    enum RebuildingRequirement: Int, Identifiable {
        case groupStage
        case all
        
        var id: Int { self.rawValue }

        var rebuildingRequirementMessage: String {
            switch self {
            case .groupStage:
                return "Si vous le souhaitez, seulement les poules seront refaites. Le tableau ne sera pas modifié."
            case .all:
                return "Tous les matchs seront re-générés. La position des têtes de série sera remise à zéro et les poules seront reconstruites."
            }
        }
    }

}

//#Preview {
//    NavigationStack {
//        TableStructureView()
//            .environment(Tournament.mock())
//            .environmentObject(DataStore.shared)
//    }
//}
