//
//  GroupStageView.swift
//  Padel Tournament
//
//  Created by Razmig Sarkissian on 02/03/2023.
//

import SwiftUI
import LeStorage

struct GroupStageView: View {
    @EnvironmentObject var dataStore: DataStore
    @Environment(Tournament.self) private var tournament
    @Bindable var groupStage: GroupStage
    @State private var confirmGroupStageStart: Bool = false
    @State private var sortingMode: GroupStageSortingMode = .auto
    @State private var confirmRemoveAll: Bool = false
    @State private var confirmResetMatch: Bool = false
    let playedMatches: [Match]
    
    @State private var runningMatches: [Match]?
    @State private var readyMatches: [Match]?
    @State private var availableToStart: [Match]?

    init(groupStage: GroupStage) {
        self.groupStage = groupStage
        self.playedMatches = groupStage.playedMatches()
    }
    
    var body: some View {
        List {
            Section {
                GroupStageScoreView(groupStage: groupStage, sortByScore: _sortByScore)
            } header: {
                if let startDate = groupStage.startDate {
                    Text(startDate.formatted(Date.FormatStyle().weekday(.wide)).capitalized + " à partir de " +  startDate.formattedAsHourMinute())
                }
            } footer: {
                HStack {
                    Spacer()
                    Button {
                        _updateSortingMode()
                    } label: {
                        Label(_sortByScore ? "tri par score" : "tri par poids", systemImage: "arrow.up.arrow.down").labelStyle(.titleOnly)
                            .underline()
                    }
                    .buttonStyle(.borderless)
                }
            }
            .headerProminence(.increased)

            MatchListView(section: "en cours", matches: runningMatches, hideWhenEmpty: true)
            MatchListView(section: "prêt à démarrer", matches: availableToStart, hideWhenEmpty: true)
            MatchListView(section: "à lancer", matches: self.readyMatches, hideWhenEmpty: true)
            MatchListView(section: "terminés", matches: groupStage.finishedMatches(playedMatches: playedMatches), isExpanded: false)
        }
        .task {
            self.runningMatches = await groupStage.asyncRunningMatches(playedMatches: playedMatches)
            self.readyMatches = await groupStage.readyMatches(playedMatches: playedMatches)
            self.availableToStart = await groupStage.availableToStart(playedMatches: playedMatches, in: runningMatches ?? [])
        }
        .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
                _groupStageMenuView()
            }
        }
        .navigationTitle(groupStage.groupStageTitle())
    }
    
    private enum GroupStageSortingMode {
        case auto
        case score
        case weight
    }
    
    private var _sortByScore: Bool {
        sortingMode == .auto ? groupStage.hasEnded() : sortingMode == .score
    }
    
    private func _updateSortingMode() {
        if sortingMode == .auto {
            if groupStage.hasEnded() {
                sortingMode = .weight
            } else {
                sortingMode = .score
            }
        } else if sortingMode == .weight {
            sortingMode = .score
        } else {
            sortingMode = .weight
        }
    }

    struct GroupStageScoreView: View {
        @EnvironmentObject var dataStore: DataStore
        let groupStage: GroupStage
        let sortByScore: Bool
        let scores: [GroupStage.TeamGroupStageScore]?
        let teams: [TeamRegistration]
        
        init(groupStage: GroupStage, sortByScore: Bool) {
            self.groupStage = groupStage
            self.sortByScore = sortByScore
            self.scores = sortByScore ? (0..<groupStage.size).compactMap({ groupStage._score(forGroupStagePosition: $0) }) : nil
            self.teams = groupStage.teams(sortByScore, scores: scores)
        }
        
        private func _teamAt(atIndex index: Int) -> TeamRegistration? {
            sortByScore ? teams[safe: index] : groupStage.teamAt(groupStagePosition: index)
        }
        
        var body: some View {
            ForEach(0..<(groupStage.size), id: \.self) { index in
                if let team = _teamAt(atIndex: index), let groupStagePosition = team.groupStagePosition {
                    NavigationLink {
                        GroupStageTeamView(groupStage: groupStage, team: team)
                    } label: {
                        VStack(alignment: .leading, spacing: 4.0) {
                            HStack(spacing: 6.0) {
                                Text("#\(groupStagePosition + 1)")
                                if groupStage.tournamentObject()?.hideWeight() == false {
                                    Text("Poids \(team.weight)")
                                }
                                Spacer()
                                if team.qualified {
                                    Text("qualifié")
                                }
                            }
                            .font(.footnote)
                            HStack {
                                VStack(alignment: .leading) {
                                    if let teamName = team.name {
                                        Text(teamName).foregroundStyle(.secondary)
                                    }
                                    ForEach(team.players()) { player in
                                        Text(player.playerLabel()).lineLimit(1)
                                    }
                                }
                                Spacer()
                                if let score = groupStage.scoreLabel(forGroupStagePosition: groupStagePosition, score: scores?.first(where: { $0.team.groupStagePosition == groupStagePosition })) {
                                    VStack(alignment: .trailing) {
                                        HStack(spacing: 0.0) {
                                            Text(score.wins)
                                            Text("/")
                                            Text(score.losses)
                                        }.font(.headline).monospacedDigit()
                                        if let setsDifference = score.setsDifference {
                                            HStack(spacing: 4.0) {
                                                Text(setsDifference)
                                                Text("sets")
                                            }.font(.footnote)
                                        }
                                        if let gamesDifference = score.gamesDifference {
                                            HStack(spacing: 4.0) {
                                                Text(gamesDifference)
                                                Text("jeux")
                                            }.font(.footnote)
                                        }
                                    }
                                }
                            }
                        }
                        .contextMenu {
                            Button("équipe présente") {
                                team.hasArrived()
                            }
                        }
                    }
                    .listRowView(isActive: team.qualified, color: .master)
                } else {
                    VStack(alignment: .leading, spacing: 0) {
                        Text("#\(index + 1)")
                            .font(.caption)
                        TeamPickerView(groupStagePosition: index, teamPicked: { team in
                            print(team.pasteData())
                            team.groupStage = groupStage.id
                            team.groupStagePosition = index
                            groupStage._matches().forEach({ $0.updateTeamScores() })
                            do {
                                try dataStore.teamRegistrations.addOrUpdate(instance: team)
                            } catch {
                                Logger.error(error)
                            }
                        })
                    }
                }
            }
        }
    }
    
    private func _groupStageMenuView() -> some View {
        Menu {
            NavigationLink {
                GroupStageNameEditionView(groupStage: groupStage)
                    .environment(tournament)
            } label: {
                Label("Modifier le nom et la taille", systemImage: "pencil")
            }
            Button("Retirer tout le monde", role: .destructive) {
                confirmRemoveAll = true
            }
            Button("Recommencer tous les matchs", role: .destructive) {
                confirmResetMatch = true
            }
        } label: {
            LabelOptions()
        }
        .confirmationDialog("Êtes-vous sûr de vouloir faire cela ?", isPresented: $confirmRemoveAll, titleVisibility: .visible) {
            Button("Oui") {
                let teams = groupStage.teams()
                teams.forEach { team in
                    team.groupStagePosition = nil
                    team.groupStage = nil
                    groupStage._matches().forEach({ $0.updateTeamScores() })
                }
                do {
                    try dataStore.teamRegistrations.addOrUpdate(contentOfs: teams)
                } catch {
                    Logger.error(error)
                }
            }
        }
        .confirmationDialog("Êtes-vous sûr de vouloir faire cela ?", isPresented: $confirmResetMatch, titleVisibility: .visible) {
            Button("Oui") {
                groupStage.buildMatches()
            }
        }

    }
    
    private func _save() {
        do {
            try dataStore.groupStages.addOrUpdate(instance: groupStage)
        } catch {
            Logger.error(error)
        }
    }
}

struct GroupStageNameEditionView: View {
    @Environment(\.dismiss) private var dismiss
    @Environment(Tournament.self) private var tournament
    @EnvironmentObject var dataStore: DataStore
    @Bindable var groupStage: GroupStage
    @State private var groupStageName: String
    @State private var presentConfirmationButton: Bool = false
    @State private var size: Int
    
    init(groupStage: GroupStage) {
        _groupStage = Bindable(groupStage)
        _groupStageName = .init(wrappedValue: groupStage.name ?? "")
        _size = .init(wrappedValue: groupStage.size)
    }
    
    var body: some View {
        Form {
            Section {
                TextField("Nom de la poule", text: $groupStageName)
                    .keyboardType(.alphabet)
                    .frame(maxWidth: .infinity)
                    .onSubmit {
                        groupStageName = groupStageName.trimmed
                        if groupStageName.isEmpty == false {
                            groupStage.name = groupStageName
                            _save()
                            dismiss()
                        }
                    }
            } footer: {
                if groupStage.name != nil {
                    HStack {
                        Spacer()
                        FooterButtonView("retirer le nom") {
                            groupStage.name = nil
                            groupStageName = ""
                            _save()
                        }
                    }
                }
            }
                        
            Section {
                LabeledContent {
                    StepperView(count: $size, minimum: minimumSize(), maximum: maximumSize())
                } label: {
                    Text("Taille de la poule")
                }
                
                if presentConfirmationButton {
                    RowButtonView("Confirmer", role: .destructive, confirmationMessage: "Tous les matchs et les équipes de cette poule seront ré-initialisés") {
                        let teams = groupStage.teams()
                        teams.forEach { team in
                            team.groupStagePosition = nil
                            team.groupStage = nil
                            groupStage._matches().forEach({ $0.updateTeamScores() })
                        }
                        do {
                            try dataStore.teamRegistrations.addOrUpdate(contentOfs: teams)
                        } catch {
                            Logger.error(error)
                        }
                        groupStage.size = size
                        groupStage.buildMatches()
                        tournament.shouldVerifyGroupStage = true
                        _save()
                        presentConfirmationButton = false
                    }
                }
            } footer: {
                Text("Vous ne pouvez réduire la taille qu'à partir de la dernière poule et augmentez la taille qu'à partir de la première poule. Une poule ne peut pas avoir moins de 3 équipes et plus d'une équipe de différence par rapport aux autres poules.")
            }
            
        }
        .onChange(of: size) {
            presentConfirmationButton = true
        }
        .navigationTitle(groupStage.groupStageTitle())
        .toolbarBackground(.visible, for: .navigationBar)
    }
    
    private func maximumSize() -> Int {
        if groupStage.index == 0 {
            return tournament.teamsPerGroupStage + 1
        }
        
        if let previousGroupStage = tournament.groupStages().first(where: { $0.index == groupStage.index - 1 }), previousGroupStage.size > groupStage.size {
            return tournament.teamsPerGroupStage + 1
        }
        
        return tournament.teamsPerGroupStage
    }

    private func minimumSize() -> Int {
        if groupStage.index == tournament.groupStageCount - 1 {
            return max(3, tournament.teamsPerGroupStage - 1)
        }
        
        if let nextGroupStage = tournament.groupStages().first(where: { $0.index == groupStage.index + 1 }), nextGroupStage.size < groupStage.size {
            return max(3, tournament.teamsPerGroupStage - 1)
        }
        
        return tournament.teamsPerGroupStage
    }

    private func _save() {
        do {
            try dataStore.groupStages.addOrUpdate(instance: groupStage)
        } catch {
            Logger.error(error)
        }
    }
}
