//
//  FederalDataViewModel.swift
//  PadelClub
//
//  Created by Razmig Sarkissian on 30/04/2024.
//

import SwiftUI
import LeStorage

@Observable
class FederalDataViewModel {
    static let shared = FederalDataViewModel()
    
    var federalTournaments: [FederalTournament] = []
    var searchedFederalTournaments: [FederalTournament] = []
    var levels: Set<TournamentLevel> = Set()
    var categories: Set<TournamentCategory> = Set()
    var ageCategories: Set<FederalTournamentAge> = Set()
    var selectedClubs: Set<String> = Set()
    var id: UUID = UUID()
    var searchAttemptCount: Int = 0
    var dayDuration: Int?
    var dayPeriod: DayPeriod = .all
    var lastError: NetworkManagerError?
    
    func filterStatus() -> String {
        var labels: [String] = []
        labels.append(contentsOf: levels.map { $0.localizedLevelLabel() }.formatList())
        labels.append(contentsOf: categories.map { $0.localizedLabel() }.formatList())
        labels.append(contentsOf: ageCategories.map { $0.localizedFederalAgeLabel() }.formatList())
        let clubNames = selectedClubs.compactMap { codeClub in
            let club: Club? = DataStore.shared.clubs.first(where: { $0.code == codeClub })
            return club?.clubTitle(.short)
        }
        
        labels.append(contentsOf: clubNames.formatList())
        if dayPeriod != .all {
            labels.append(dayPeriod.localizedDayPeriodLabel())
        }
        if let dayDuration {
            labels.append("max " + dayDuration.formatted() + " jour" + dayDuration.pluralSuffix)
        }
        return labels.joined(separator: ", ")
    }
    
    var searchedClubs: [FederalClub] {
        searchedFederalTournaments.compactMap { ft in
            ft.federalClub
        }.uniqued { fc in
            fc.federalClubCode
        }.sorted(by: \.federalClubName)
    }
    
    func selectedClub() -> Club? {
        if  selectedClubs.isEmpty == false {
            return DataStore.shared.clubs.first(where: { $0.code == selectedClubs.first! })
        } else {
            return nil
        }
    }
    
    func removeFilters() {
        levels.removeAll()
        categories.removeAll()
        ageCategories.removeAll()
        selectedClubs.removeAll()
        dayPeriod = .all
        dayDuration = nil
        id = UUID()
    }
    
    func areFiltersEnabled() -> Bool {
        (levels.isEmpty && categories.isEmpty && ageCategories.isEmpty && selectedClubs.isEmpty && dayPeriod == .all && dayDuration == nil) == false
    }
    
    var filteredFederalTournaments: [FederalTournamentHolder] {
        filteredFederalTournaments(from: federalTournaments)
    }
    
    var filteredSearchedFederalTournaments: [FederalTournamentHolder] {
        filteredFederalTournaments(from: searchedFederalTournaments)
    }
    
    func filteredFederalTournaments(from tournaments: [any FederalTournamentHolder]) -> [FederalTournamentHolder] {
        tournaments.filter({ tournament in
            (levels.isEmpty || tournament.tournaments.anySatisfy({ levels.contains($0.level) }))
            &&
            (categories.isEmpty || tournament.tournaments.anySatisfy({ categories.contains($0.category) }))
            &&
            (ageCategories.isEmpty || tournament.tournaments.anySatisfy({ ageCategories.contains($0.age) }))
            &&
            (selectedClubs.isEmpty || (tournament.codeClub != nil && selectedClubs.contains(tournament.codeClub!)))
            &&
            (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod))
            &&
            (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration))
        })
    }
    
    func countForTournamentBuilds(from tournaments: [any FederalTournamentHolder]) -> Int {
        tournaments.filter({ tournament in
            (selectedClubs.isEmpty || (tournament.codeClub != nil && selectedClubs.contains(tournament.codeClub!)))
            &&
            (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod))
            &&
            (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration))
        })
        .flatMap { $0.tournaments }
        .filter {
            (levels.isEmpty || levels.contains($0.level))
            &&
            (categories.isEmpty || categories.contains($0.category))
            &&
            (ageCategories.isEmpty || ageCategories.contains($0.age))
        }
        .count
    }
    
    func buildIsValid(_ build: any TournamentBuildHolder) -> Bool {
        (levels.isEmpty || levels.contains(build.level))
        &&
        (categories.isEmpty || categories.contains(build.category))
        &&
        (ageCategories.isEmpty || ageCategories.contains(build.age))
    }
    
    func isTournamentValidForFilters(_ tournament: Tournament) -> Bool {
        if tournament.isDeleted { return false }
        let firstPart = (levels.isEmpty || levels.contains(tournament.level))
        &&
        (categories.isEmpty || categories.contains(tournament.category))
        &&
        (ageCategories.isEmpty || ageCategories.contains(tournament.age))
        &&
        (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod))
        &&
        (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration))

        if let codeClub = tournament.club()?.code {
            return firstPart && (selectedClubs.isEmpty || selectedClubs.contains(codeClub))
        } else {
            return firstPart
        }
    }
    
    func isFederalTournamentValidForFilters(_ tournament: FederalTournament, build: any TournamentBuildHolder) -> Bool {
        (levels.isEmpty || levels.contains(build.level))
        &&
        (categories.isEmpty || categories.contains(build.category))
        &&
        (ageCategories.isEmpty || ageCategories.contains(build.age))
        &&
        (selectedClubs.isEmpty || (tournament.codeClub != nil && selectedClubs.contains(tournament.codeClub!)))
        &&
        (dayPeriod == .all || (dayPeriod != .all && dayPeriod == tournament.dayPeriod))
        &&
        (dayDuration == nil || (dayDuration != nil && dayDuration == tournament.dayDuration))
    }
                                  
    func gatherTournaments(clubs: [Club], startDate: Date, endDate: Date? = nil) async throws {
        try await clubs.filter { $0.code != nil }.concurrentForEach { club in
            let newTournaments = try await NetworkFederalService.shared.getClubFederalTournaments(page: 0, tournaments: [], club: club.name, codeClub: club.code!, startDate: startDate, endDate: endDate)
            
            newTournaments.forEach { tournament in
                if self.federalTournaments.contains(where: { $0.id == tournament.id }) == false {
                    self.federalTournaments.append(tournament)
                }
            }
        }
    }
}

struct FederalClub: Identifiable {
    var id: String { federalClubCode }
    var federalClubCode: String
    var federalClubName: String
}
