//
//  AddTeamView.swift
//  PadelClub
//
//  Created by Razmig Sarkissian on 15/07/2024.
//

import SwiftUI
import LeStorage
import CoreData

struct AddTeamView: View {
    
    @Environment(\.dismiss) var dismiss

    private var fetchRequest: FetchRequest<ImportedPlayer>
    private var fetchPlayers: FetchedResults<ImportedPlayer> { fetchRequest.wrappedValue }

    let tournament: Tournament
    var cancelShouldDismiss: Bool = false
    enum FocusField: Hashable {
      case pasteField
    }

    @FocusState private var focusedField: FocusField?
    @State private var searchField: String = ""
    @State private var presentSearch: Bool = false
    @State private var presentPlayerSearch: Bool = false
    @State private var presentPlayerCreation: Bool = false
    @State private var presentImportView: Bool = false
    @State private var createdPlayers: Set<PlayerRegistration> = Set()
    @State private var createdPlayerIds: Set<String> = Set()
    @State private var editedTeam: TeamRegistration?
    @State private var pasteString: String?
    @State private var selectionSearchField: String?
    @State private var autoSelect: Bool = false
    @State private var presentationCount: Int = 0
    @State private var confirmDuplicate: Bool = false
    @State private var homonyms: [PlayerRegistration] = []
    @State private var confirmHomonym: Bool = false
    @State private var editableTextField: String = ""
    @State private var textHeight: CGFloat = 100 // Default height
    @State private var hitsForSearch: [Int: Int] = [:]
    @State private var searchForHit: Int = 0
    @State private var displayWarningNotEnoughCharacter: Bool = false
    @State private var testMessageIndex: Int = 0
    @State private var presentLocalMultiplayerSearch: Bool = false
        
    var tournamentStore: TournamentStore? {
        return self.tournament.tournamentStore
    }

    init(tournament: Tournament, pasteString: String? = nil, editedTeam: TeamRegistration? = nil) {
        self.tournament = tournament
        _editedTeam = .init(wrappedValue: editedTeam)
        if let team = editedTeam {
            var createdPlayers: Set<PlayerRegistration> = Set()
            var createdPlayerIds: Set<String> = Set()

            team.unsortedPlayers().forEach { player in
                createdPlayers.insert(player)
                createdPlayerIds.insert(player.id)
            }
            
            _createdPlayers = .init(wrappedValue: createdPlayers)
            _createdPlayerIds = .init(wrappedValue: createdPlayerIds)
        }
        
        let request: NSFetchRequest<ImportedPlayer> = ImportedPlayer.fetchRequest()
        request.sortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)]
        request.fetchLimit = 1000
        if let pasteString {
            _pasteString = .init(wrappedValue: pasteString)
            request.predicate = SearchViewModel.pastePredicate(pasteField: pasteString, mostRecentDate: tournament.rankSourceDate, filterOption: tournament.tournamentCategory.playerFilterOption)
            _autoSelect = .init(wrappedValue: true)
            _editableTextField = .init(wrappedValue: pasteString)
            _textHeight = .init(wrappedValue: Self._calculateHeight(text: pasteString))
            cancelShouldDismiss = true
        }
        
        fetchRequest = FetchRequest(fetchRequest: request, animation: .default)
    }
    
    var selectionLimit: Int {
        if tournament.isAnimation() {
            return -1
        } else {
            return tournament.significantPlayerCount() - _currentSelectionIds().count
        }
    }
    
    var body: some View {
        if let pasteString, pasteString.isEmpty == false, fetchPlayers.isEmpty == false {
            computedBody
                .searchable(text: $searchField, placement: .navigationBarDrawer(displayMode: .always), prompt: Text("Chercher dans les résultats"))
        } else {
            computedBody
        }
    }
    
    var computedBody: some View {
        List(selection: $createdPlayerIds) {
            _buildingTeamView()
        }
        .onReceive(fetchPlayers.publisher.count()) { receivedCount in  // <-- here
            if let pasteString, pasteString.isEmpty == false, count == 2, autoSelect == true {
                fetchPlayers.filter { hitForSearch($0, pasteString) >= hitTarget }.sorted(by: { hitForSearch($0, pasteString) > hitForSearch($1, pasteString) }).forEach { player in
                    createdPlayerIds.insert(player.license!)
                }
                autoSelect = false
            }
        }
        .overlay(alignment: .bottom) {
            if displayWarningNotEnoughCharacter {
                Text("2 lettres mininum")
                    .toastFormatted()
                    .animation(.easeInOut(duration: 2.0), value: displayWarningNotEnoughCharacter)
                    .onAppear {
                        DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
                            displayWarningNotEnoughCharacter = false
                        }
                    }
            }
        }

        .alert("Présence d'homonyme", isPresented: $confirmHomonym) {
            Button("Créer l'équipe quand même") {
                _createTeam(checkDuplicates: false, checkHomonym: false)
            }
            
            Button("Annuler", role: .cancel) {
                confirmHomonym = false
            }

        } message: {
            let plural : String = homonyms.count > 1 ? "nt chacun" : ""
            Text(homonyms.map({ $0.playerLabel() }).joined(separator: ", ") + " possède" + plural + " au moins un homonyme dans la base fédérale, vérifiez bien leur licence.")
        }
        .alert("Cette équipe existe déjà", isPresented: $confirmDuplicate) {
            Button("Créer l'équipe quand même") {
                _createTeam(checkDuplicates: false, checkHomonym: true)
            }
            
            Button("Annuler", role: .cancel) {
                confirmDuplicate = false
            }

        } message: {
            Text("Cette équipe existe déjà dans votre liste d'inscription.")
        }
        .sheet(isPresented: $presentLocalMultiplayerSearch) {
            NavigationStack {
                SelectablePlayerListView(allowSelection: -1, isPresented: false, searchField: searchField, dataSet: .club, filterOption: _filterOption(), showFemaleInMaleAssimilation: tournament.tournamentCategory.showFemaleInMaleAssimilation) { players in
                    players.forEach { player in
                        let newPlayer = PlayerRegistration(importedPlayer: player)
                        newPlayer.setComputedRank(in: tournament)
                        createdPlayers = Set<PlayerRegistration>()
                        createdPlayerIds = Set<String>()
                        createdPlayers.insert(newPlayer)
                        createdPlayerIds.insert(newPlayer.id)
                        _createTeam(checkDuplicates: false, checkHomonym: false)
                    }

                } contentUnavailableAction: { searchViewModel in
                    presentLocalMultiplayerSearch = false
                    selectionSearchField = searchViewModel.searchText
                }
            }
            .tint(.master)

        }
        .sheet(isPresented: $presentPlayerSearch) {
            NavigationStack {
                SelectablePlayerListView(allowSelection: selectionLimit, isPresented: true, searchField: searchField, filterOption: _filterOption(), showFemaleInMaleAssimilation: tournament.tournamentCategory.showFemaleInMaleAssimilation) { players in
                    players.forEach { player in
                        let newPlayer = PlayerRegistration(importedPlayer: player)
                        newPlayer.setComputedRank(in: tournament)
                        createdPlayers.insert(newPlayer)
                        createdPlayerIds.insert(newPlayer.id)
                    }
                } contentUnavailableAction: { searchViewModel in
                    presentPlayerSearch = false
                    selectionSearchField = searchViewModel.searchText
                }
            }
            .tint(.master)
        }
        .sheet(isPresented: $presentPlayerCreation) {
            PlayerPopoverView(sex: _addPlayerSex()) { p in
                p.setComputedRank(in: tournament)
                createdPlayers.insert(p)
                createdPlayerIds.insert(p.id)
            }
            .tint(.master)
        }
        .sheet(item: $selectionSearchField, onDismiss: {
            selectionSearchField = nil
        }) { selectionSearchField in
            PlayerPopoverView(source: selectionSearchField, sex: _addPlayerSex()) { p in
                p.setComputedRank(in: tournament)
                createdPlayers.insert(p)
                createdPlayerIds.insert(p.id)
            }
            .tint(.master)
        }
        .toolbar {
            ToolbarItem(placement: .cancellationAction) {
                Button("Annuler", role: .cancel) {
                    dismiss()
                }
            }

            if pasteString == nil {
                ToolbarItem(placement: .bottomBar) {
                    PasteButton(payloadType: String.self) { strings in
                        let first = strings.first ?? ""
                        DispatchQueue.main.async {
                            self.handlePasteString(first)
                        }
                    }
                    .disabled(_limitPlayerCount())
                    .foregroundStyle(.master)
                    .labelStyle(.titleAndIcon)
                    .buttonBorderShape(.capsule)
                }
            }
            
            ToolbarItem(placement: .topBarTrailing) {
                Button {
                    let generalString = UIPasteboard.general.string ?? ""
                                        
                    #if targetEnvironment(simulator)
                    let s = testMessages[testMessageIndex % testMessages.count]
                    handlePasteString(s)
                    testMessageIndex += 1
                    #else
                    handlePasteString(generalString)
                    #endif
                } label: {
                    Label("Coller", systemImage: "doc.on.clipboard").labelStyle(.iconOnly)
                }
                .disabled(_limitPlayerCount())
                .foregroundStyle(.master)
                .labelStyle(.iconOnly)
                .buttonBorderShape(.capsule)
            }

        }
        .navigationBarBackButtonHidden(true)
        .toolbarBackground(.visible, for: .navigationBar)
        .toolbarBackground(.automatic, for: .bottomBar)
        .navigationBarTitleDisplayMode(.inline)
        .navigationTitle(editedTeam == nil ? "Ajouter une équipe" : "Modifier l'équipe")
        .environment(\.editMode, Binding.constant(EditMode.active))
    }
    
    private func _isEditingTeam() -> Bool {
        createdPlayerIds.isEmpty == false || editedTeam != nil || pasteString != nil
    }
    
    var unsortedPlayers: [PlayerRegistration] {
        tournament.unsortedPlayers()
    }
    
    private func _limitPlayerCount() -> Bool {
        if tournament.isAnimation() {
            return false
        }
        return _currentSelection().count >= tournament.significantPlayerCount()
    }
    
    @ViewBuilder
    private func _managementView() -> some View {
        Section {
            RowButtonView("Ajouter via la base fédérale") {
                presentPlayerSearch = true
            }
            .disabled(_limitPlayerCount())
        } footer: {
            if let rankSourceDate = tournament.rankSourceDate {
                Text("Cherchez dans la base fédérale de \(rankSourceDate.monthYearFormatted), vous y trouverez tous les joueurs ayant participé à au moins un tournoi dans les 12 derniers mois.")
            }
        }
        
        if tournament.isAnimation(), createdPlayers.isEmpty == true {
            Section {
                RowButtonView("Ajouter plusieurs joueurs du club") {
                    presentLocalMultiplayerSearch = true
                }
            } footer: {
                Text("Crée une équipe par joueur sélectionné")
            }
        }
        
        Section {
            RowButtonView("Créer un non classé / non licencié") {
                if let pasteString, pasteString.isEmpty == false {
                    selectionSearchField = pasteString
                } else {
                    presentPlayerCreation = true
                }
            }
            .disabled(_limitPlayerCount())
        } footer: {
            Text("Si le joueur n'a pas encore de licence ou n'a pas encore participé à une compétition, vous pouvez le créer vous-même.")
        }
    }
    
    private func _addPlayerSex() -> Int {
        switch tournament.tournamentCategory {
        case .men, .unlisted:
            return 1
        case .women:
            return 0
        case .mix:
            return 1
        }

    }
    
    private func _filterOption() -> PlayerFilterOption {
        return tournament.tournamentCategory.playerFilterOption
    }
        
    private func _currentSelection() -> Set<PlayerRegistration> {
        var currentSelection = Set<PlayerRegistration>()
        createdPlayerIds.compactMap { id in
            fetchPlayers.first(where: { id == $0.license })
        }.forEach { player in
            let player = PlayerRegistration(importedPlayer: player)
            player.setComputedRank(in: tournament)
            currentSelection.insert(player)
        }
        
        createdPlayerIds.compactMap { id in
            createdPlayers.first(where: { id == $0.id })
        }.forEach {
            currentSelection.insert($0)
        }
        return currentSelection
    }

    private func _currentSelectionIds() -> [String?] {
        var currentSelection = [String?]()
        createdPlayerIds.compactMap { id in
            fetchPlayers.first(where: { id == $0.license })
        }.forEach { player in
            currentSelection.append(player.license)
        }
        
        createdPlayerIds.compactMap { id in
            createdPlayers.first(where: { id == $0.id })
        }.forEach {
            currentSelection.append($0.licenceId)
        }
        return currentSelection
    }
    
    private func _isDuplicate() -> Bool {
        if tournament.isAnimation() { return false }
        let ids : [String?] = _currentSelectionIds()
        if tournament.selectedSortedTeams().anySatisfy({ $0.containsExactlyPlayerLicenses(ids) }) {
            return true
        }
        return false
    }
    
    private func _createTeam(checkDuplicates: Bool, checkHomonym: Bool) {
        if checkDuplicates && _isDuplicate() {
            confirmDuplicate = true
            return
        }
        
        let players = _currentSelection()
        
        if checkHomonym {
            homonyms = players.filter({ $0.hasHomonym() })
            if homonyms.isEmpty == false {
                confirmHomonym = true
                return
            }
        }
        
        let team = tournament.addTeam(players)
        self.tournamentStore?.teamRegistrations.addOrUpdate(instance: team)
        self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players)
        
        pasteString = nil
        editableTextField = ""
        createdPlayers.removeAll()
        createdPlayerIds.removeAll()
        
        if team.players().count > 1 {
            dismiss()
        } else {
            editedTeam = team
            team.unsortedPlayers().forEach { player in
                createdPlayers.insert(player)
                createdPlayerIds.insert(player.id)
            }
        }
    }
    
    private func _updateTeam(checkDuplicates: Bool) {
        guard let editedTeam else { return }
        if checkDuplicates && _isDuplicate() {
            confirmDuplicate = true
            return
        }

        let players = _currentSelection()
        editedTeam.updatePlayers(players, inTournamentCategory: tournament.tournamentCategory)
        self.tournamentStore?.teamRegistrations.addOrUpdate(instance: editedTeam)
        self.tournamentStore?.playerRegistrations.addOrUpdate(contentOfs: players)
        
        pasteString = nil
        editableTextField = ""

        if editedTeam.players().count > 1 {
            dismiss()
        }
    }
    
    // Calculating the height based on the content of the TextEditor
    static private func _calculateHeight(text: String) -> CGFloat {
        let size = CGSize(width: UIScreen.main.bounds.width - 32, height: .infinity)
        let attributes: [NSAttributedString.Key: Any] = [.font: UIFont.systemFont(ofSize: 17)]
        let boundingRect = text.boundingRect(
            with: size,
            options: .usesLineFragmentOrigin,
            attributes: attributes,
            context: nil
        )
        return max(boundingRect.height + 20, 40) // Add some padding and set a minimum height
    }
    
    @ViewBuilder
    private func _buildingTeamView() -> some View {
            if let pasteString {
                Section {
                    TextEditor(text: $editableTextField)
                        .frame(height: textHeight)
                        .onChange(of: editableTextField) {
                            textHeight = Self._calculateHeight(text: pasteString)
                        }
                        .focused($focusedField, equals: .pasteField)
                        .toolbar {
                            ToolbarItemGroup(placement: .keyboard) {
                                Button("Fermer", role: .cancel) {
                                    self.editableTextField = pasteString
                                    self.focusedField = nil
                                }
                                Spacer()
                                Button("Chercher") {
                                    if editableTextField.count > 1 {
                                        self.handlePasteString(editableTextField)
                                        self.focusedField = nil
                                    } else {
                                        self.displayWarningNotEnoughCharacter = true
                                    }
                                }
                                .buttonStyle(.bordered)
                            }
                        }
                } header: {
                    Text("Contenu du presse-papier")
                } footer: {
                    HStack {
                        FooterButtonView("éditer") {
                            self.focusedField = .pasteField
                        }
                        Spacer()
                        FooterButtonView("effacer le texte") {
                            self.focusedField = nil
                            self.editableTextField = ""
                            self.pasteString = nil
                        }
                    }
                }
            }
            
            Section {
                ForEach(createdPlayerIds.sorted(), id: \.self) { id in
                    if let p = createdPlayers.first(where: { $0.id == id }) {
                        VStack(alignment: .leading, spacing: 0) {
                            if let player = unsortedPlayers.first(where: { ($0.licenceId == p.licenceId && $0.licenceId != nil) }), editedTeam?.includes(player: player) == false {
                                Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
                            }
                            if tournament.isPlayerAgeInadequate(player: p) {
                                Text("Âge invalide !").foregroundStyle(.logoRed).bold()
                            }
                            if tournament.isPlayerRankInadequate(player: p) {
                                Text("Trop bien classé !").foregroundStyle(.logoRed).bold()
                            }
                            PlayerView(player: p).tag(p.id)
                                .environment(tournament)
                        }
                    }
                    if let p = fetchPlayers.first(where: { $0.license == id }) {
                        VStack(alignment: .leading, spacing: 0) {
                            if let pasteString, pasteString.isEmpty == false, unsortedPlayers.first(where: { $0.licenceId == p.license }) != nil {
                                Text("Déjà inscrit !").foregroundStyle(.logoRed).bold()
                            }
                            if tournament.isPlayerAgeInadequate(player: p) {
                                Text("Âge invalide !").foregroundStyle(.logoRed).bold()
                            }
                            if tournament.isPlayerRankInadequate(player: p) {
                                Text("Trop bien classé !").foregroundStyle(.logoRed).bold()
                            }
                            ImportedPlayerView(player: p).tag(p.license!)
                        }
                    }
                }
                if editedTeam == nil {
                    if createdPlayerIds.isEmpty {
                        RowButtonView("Bloquer une place") {
                            _createTeam(checkDuplicates: false, checkHomonym: false)
                        }
                    } else {
                        RowButtonView("Ajouter l'équipe") {
                            _createTeam(checkDuplicates: true, checkHomonym: true)
                        }
                    }
                } else {
                    RowButtonView("Confirmer") {
                        _updateTeam(checkDuplicates: false)
                        dismiss()
                    }
                }
            } header: {
                let _currentSelection = _currentSelection()
                let selectedSortedTeams = tournament.selectedSortedTeams()
                let rank = _currentSelection.map {
                    $0.computedRank
                }.reduce(0, +)
                let teamIndex = selectedSortedTeams.firstIndex(where: { $0.weight >= rank }) ?? selectedSortedTeams.count
                if _currentSelection.isEmpty == false, tournament.hideWeight() == false, rank > 0 {
                    HStack(spacing: 16.0) {
                        VStack(alignment: .leading, spacing: 0) {
                            Text("Rang").font(.caption)
                            Text("#" + (teamIndex + 1).formatted())
                        }

                        VStack(alignment: .leading, spacing: 0) {
                            Text("Poids").font(.caption)
                            Text(rank.formatted())
                        }
                        Spacer()
                        VStack(alignment: .trailing, spacing: 0) {
                            Text("").font(.caption)
                            Text(tournament.cutLabel(index: teamIndex, teamCount: selectedSortedTeams.count))
                        }
                    }
//                } else {
//                    Text("Préparation de l'équipe")
                }
            } footer: {
                if _limitPlayerCount() {
                    Text("Taille maximum de l'équipe atteinte, supprimer un joueur pour pouvoir en rajouter un autre").foregroundStyle(.logoRed)
                }
            }

            
        if let pasteString, pasteString.isEmpty == false {
                let sortedPlayers = _searchFilteredPlayers()

                if sortedPlayers.isEmpty {
                    ContentUnavailableView {
                        Label("Aucun résultat", systemImage: "person.2.slash")
                    } description: {
                        Text("Aucun joueur classé n'a été trouvé dans ce message. Attention, si un joueur n'a pas joué de tournoi dans les 12 derniers, Padel Club ne pourra pas le trouver.")
                    } actions: {
                        RowButtonView("Créer un joueur non classé") {
                            selectionSearchField = pasteString
                        }
                               
                        RowButtonView("Chercher dans la base") {
                            presentPlayerSearch = true
                        }

                        RowButtonView("Effacer cette recherche") {
                            self.pasteString = nil
                            self.editableTextField = ""
                        }
                    }

                } else {
                    _listOfPlayers(searchFilteredPlayers: sortedPlayers, pasteString: pasteString)
                }
            } else {
                _managementView()
            }
    }
    
    @MainActor
    func hitForSearch(_ ip: ImportedPlayer, _ pasteString: String?) -> Int {
        guard let pasteString else { return 0 }
        let _searchForHit = pasteString.hashValue

        if searchForHit != _searchForHit {
            DispatchQueue.main.async {
                searchForHit = _searchForHit
                hitsForSearch = [:]
            }
        }

        let value = hitsForSearch[ip.id.hashValue]
        if let value {
            return value
        } else {
            let hit = ip.hitForSearch(pasteString)
            DispatchQueue.main.async {
                hitsForSearch[ip.id.hashValue] = hit
            }
            return hit
        }
    }

    private var count: Int {
        return fetchPlayers.filter { hitForSearch($0, pasteString) >= hitTarget }.count
    }

    private var hitTarget: Int {
        if (pasteString?.matches(of: /[1-9][0-9]{5,7}/).count ?? 0) > 1 {
            if fetchPlayers.filter({ hitForSearch($0, pasteString) == 100 }).count == 2 { return 100 }
        } else {
            return 2
        }
        return 1
    }
        
    private func handlePasteString(_ first: String) {
        if first.isEmpty == false {
            fetchPlayers.nsPredicate = SearchViewModel.pastePredicate(pasteField: first, mostRecentDate: SourceFileManager.shared.mostRecentDateAvailable, filterOption: _filterOption())
            fetchPlayers.nsSortDescriptors = [NSSortDescriptor(keyPath: \ImportedPlayer.rank, ascending: true)]
            autoSelect = true
        }
        pasteString = first
        editableTextField = first
        textHeight = Self._calculateHeight(text: first)
    }

    @ViewBuilder
    private func _listOfPlayers(searchFilteredPlayers: [ImportedPlayer], pasteString: String) -> some View {
        let sortedPlayers = _sortedPlayers(searchFilteredPlayers: searchFilteredPlayers, pasteString: pasteString)
        
        Section {
            ForEach(sortedPlayers) { player in
                ImportedPlayerView(player: player).tag(player.license!)
                //Text(player.getLastName() + " " + player.getFirstName()).tag(player.license!)
            }
        } header: {
            Text(sortedPlayers.count.formatted() + " résultat" + sortedPlayers.count.pluralSuffix)
        }
    }

    
    private func _searchFilteredPlayers() -> [ImportedPlayer] {
        if searchField.isEmpty {
            return Array(fetchPlayers)
        } else {
            return fetchPlayers.filter({ $0.contains(searchField) })
        }
    }
    
    private func _sortedPlayers(searchFilteredPlayers: [ImportedPlayer], pasteString: String) -> [ImportedPlayer] {
        return searchFilteredPlayers.sorted(by: { hitForSearch($0, pasteString) > hitForSearch($1, pasteString) })
    }
}

let testMessages = [
    "Anthony dovetta ( 3620578 K )et christophe capeau ( 4666443v)",
"""
ok merci, il s'agit de :
Olivier Seguin - licence 5033439
JPascal Bondierlange - licence :
6508359 С
Cordialement
""",
"""
Bonsoir Lise, peux tu nous inscrire pour le 250 hommes du 15 au 17 novembre ?
Paires DESCHAMPS/PARDO. En te remerciant. Bonne soirée
Franck
""",
"""
Coucou inscription pour le tournoi du 11 /
12 octobre
Dumoutier/ Liagre Charlotte
Merci de ta confirmation"
""",
"""
Anthony Contet 6081758f
Tullou Benjamin 8990867f
""",
"""
Sms Julien La Croix +33622886688
Salut Raz, c'est    !   Ju Lacroix J'espère que tu vas bien depuis le temps! Est-ce que tu peux nous inscrire au 1000 de Bandol avec Derek Gerson stp? 
"""
]
