//
//  PadelClubView.swift
//  PadelClub
//
//  Created by Razmig Sarkissian on 01/03/2024.
//

import SwiftUI
import LeStorage
import Foundation

struct PadelClubView: View {
    @State private var uuid: UUID = UUID()
    @EnvironmentObject var dataStore: DataStore
    
    var lastDataSource: String? {
        dataStore.appSettings.lastDataSource
    }
    
    @Environment(\.managedObjectContext) private var viewContext
    @Environment(ImportObserver.self) private var importObserver: ImportObserver

    @FetchRequest(
        sortDescriptors: [],
        animation: .default)
    private var players: FetchedResults<ImportedPlayer>

    var _mostRecentDateAvailable: Date? {
        SourceFileManager.shared.mostRecentDateAvailable
    }
    
    var _lastDataSourceDate: Date? {
        guard let lastDataSource else { return nil }
        return URL.importDateFormatter.date(from: lastDataSource)
    }
    
    var body: some View {
        List {
            let monthData = dataStore.monthData.sorted(by: \.creationDate).reversed()
            
            if let currentMonth = monthData.first, currentMonth.incompleteMode {
                Section {
                    Text("Attention, depuis Août 2024, les données fédérales publiques des joueurs (messieurs) récupérables sont incomplètes car limitées aux 80 000 premiers joueurs.")
                    
                    if currentMonth.maleUnrankedValue == nil {
                        Text("Le rang d'un joueur non-classé n'est donc pas calculable pour le moment, Padel Club utilisera une valeur par défaut de 90 000.")
                    }
                    
                    Text("Un classement souligné comme ci-dessous indiquera que l'information provient d'un mois précédent.")
                    
                    Text("10342ème")
                        .background {
                            UnderlineView()
                        }
                }
            }
            
            #if DEBUG
            /*
             ["36435", "BOUNOUA", "walid", "France", "3311600", "15,00", "Non", "2", "AUVERGNE RHONE-ALPES", "50 73 0046", "CHAMBERY TC"]
             ["36435", "BRUL…", "Romain", "France", "2993139", "15,00", "Non", "2", "NOUVELLE AQUITAINE", "59 33 0447", "SAINT LOUBES TC"]

             */
            
            Section {
                RowButtonView("Retry Anonymous") {
                    await _retryAnonymous()
                }
            }
            
            Section {
                RowButtonView("Write anonymous") {
                    _writeAnonymous()
                }
            }
            
            Section {
                RowButtonView("Exporter en csv") {
                    await _exportCsv()
                }
            }
            #endif

            if let _lastDataSourceDate {
                Section {
                    LabeledContent {
                        Image(systemName: "checkmark")
                    } label: {
                        Text(_lastDataSourceDate.monthYearFormatted)
                        Text("Classement mensuel utilisé")
                    }
                } footer: {
                    FooterButtonView("Ré-importer") {
                        _startImporting()
                    }
                }
                
                if let mostRecentDateAvailable = SourceFileManager.shared.mostRecentDateAvailable, _lastDataSourceDate.isEarlierThan(mostRecentDateAvailable), importObserver.isImportingFile() == false {
                    Section {
                        RowButtonView("Importer \(mostRecentDateAvailable.monthYearFormatted)") {
                            _startImporting()
                        }
                    }
                }
            }
            
            Section {
                RowButtonView("Télécharger les données précédentes", role: .destructive) {
                    await _downloadPreviousDate()
                }
            } footer: {
                Text("Padel Club va récupérer les données des mois précédents")
            }
            
            if importObserver.isImportingFile() {
                ContentUnavailableView("Importation en cours", systemImage: "server.rack", description: Text("Une importation des données fédérales publiques est en cours, veuillez patienter."))
            } else if (players.isEmpty || lastDataSource == nil) {
                ContentUnavailableView {
                    Label("Aucun joueur importé", systemImage: "person.slash")
                } description: {
                    Text("Padel Club peut importer toutes les données publiques de la FFT concernant tous les compétiteurs et compétitrices.")
                } actions: {
                    if let _mostRecentDateAvailable {
                        RowButtonView("Importer \(_mostRecentDateAvailable.monthYearFormatted)") {
                            _startImporting()
                        }
                    }
                }
            }

            ForEach(monthData) { monthData in
                Section {
                    LabeledContent {
                        if let maleCount = monthData.maleCount {
                            Text(maleCount.formatted())
                        }
                    } label: {
                        Text("Messieurs")
                    }
                    LabeledContent {
                        if let femaleCount = monthData.femaleCount {
                            Text(femaleCount.formatted())
                        }
                    } label: {
                        Text("Dames")
                    }

                    if monthData.incompleteMode {
                        LabeledContent {
                            if let anonymousCount = monthData.anonymousCount {
                                Text(anonymousCount.formatted())
                            }
                        } label: {
                            Text("Joueurs anonymes")
                        }
                    }

                    LabeledContent {
                        if let maleUnrankedValue = monthData.maleUnrankedValue {
                            Text(maleUnrankedValue.formatted())
                        } else {
                            Text(90_000.formatted())
                        }
                    } label: {
                        Text("Rang d'un non classé")
                        if monthData.incompleteMode && monthData.maleUnrankedValue == nil {
                            Text("Messieurs (estimation car incomplet)")
                        } else {
                            Text("Messieurs")
                        }
                    }
                    LabeledContent {
                        if let femaleUnrankedValue = monthData.femaleUnrankedValue {
                            Text(femaleUnrankedValue.formatted())
                        }
                    } label: {
                        Text("Rang d'une non classée")
                        Text("Dames")
                    }
                    #if DEBUG
                    RowButtonView("recalc") {
                        await _calculateLastRank(dataSource: monthData.monthKey)
                    }
                    #endif
                } header: {
                    HStack {
                        Text(monthData.monthKey)
                        Spacer()
                        Text(monthData.total().formatted() + " joueurs")
                    }
                }
            }
        }
        .id(uuid)
        .headerProminence(.increased)
        .navigationTitle("Données fédérales")
    }
    
    @ViewBuilder
    func _activityStatus() -> some View {
        if importObserver.checkingFiles || importObserver.isImportingFile() {
            HStack(spacing: 20) {
                ProgressView()
                Text(importObserver.currentlyImportingLabel())
            }
        } else if let _mostRecentDateAvailable {
            if _mostRecentDateAvailable > _lastDataSourceDate ?? .distantPast {
                Text(_mostRecentDateAvailable.monthYearFormatted + " disponible à l'importation")
            } else {
                Label(_mostRecentDateAvailable.monthYearFormatted, systemImage: "checkmark").labelStyle(.titleAndIcon)
            }
        } else {
            Text("Aucune donnée disponible")
        }
    }
    
    private func _startImporting() {
        let importingDate = SourceFileManager.shared.mostRecentDateAvailable
        importObserver.currentImportDate = importingDate
        Task {
            if let importingDate {
                let lastDataSource = await FileImportManager.shared.importDataFromFFT(importingDate: importingDate)
                dataStore.appSettings.lastDataSource = lastDataSource
                dataStore.appSettingsStorage.write()
                if let lastDataSource, let mostRecentDate = URL.importDateFormatter.date(from: lastDataSource) {
                    await MonthData.calculateCurrentUnrankedValues(fromDate: mostRecentDate)
                }
                viewContext.refreshAllObjects()
            }
            await MainActor.run {
                importObserver.currentImportDate = nil
            }
        }
    }
    
    private func _downloadPreviousDate() async {
        await SourceFileManager.shared.getAllFiles(initialDate: "08-2022")
        self.uuid = UUID()
    }
        
#if DEBUG
    private func _calculateMonthData(dataSource: String?) async {
        if let dataSource, let mostRecentDate = URL.importDateFormatter.date(from: dataSource) {
            await MonthData.calculateCurrentUnrankedValues(fromDate: mostRecentDate)
        }
    }

    private func _calculateLastRank(dataSource: String) async {
        await _calculateMonthData(dataSource: dataSource)
    }
    
    private func _writeAnonymous() {
        for fileURL in SourceFileManager.shared.anonymousFiles() {
            let lastDateString = URL.importDateFormatter.string(from: fileURL.dateFromPath)
            let sourceType = fileURL.manData ? SourceFile.messieurs : SourceFile.dames
            let dateString = ["CLASSEMENT-PADEL", sourceType.rawValue,  lastDateString].filter({ $0.isEmpty == false }).joined(separator: "-") + "." + "csv"

            let documentsUrl:URL =  (FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first as URL?)!
            let destinationFileUrl = documentsUrl.appendingPathComponent("rankings").appendingPathComponent("\(dateString)")

            updateCSVFile(sourceCSVURL: destinationFileUrl, updatedCSVURL: fileURL)
        }
    }
    
    private func _retryAnonymous() async {
        for fileURL in SourceFileManager.shared.anonymousFiles() {
            let players = FileImportManager.shared.readCSV(inputFile: fileURL)
            var anonymousPlayers = players
            print("before anonymousPlayers.count", anonymousPlayers.count)
            await fetchPlayersDataSequentially(for: &anonymousPlayers)
            print("after beach anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }
                .count)
            SourceFileManager.shared.exportToCSV("anonymes", players: anonymousPlayers, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath)
        }
    }
    
    private func _exportCsv() async {
        for fileURL in SourceFileManager.shared.jsonFiles() {
            let decoder = JSONDecoder()
            decoder.userInfo[.maleData] = fileURL.manData
            
            do {
                let data = try Data(contentsOf: fileURL)
                let players = try decoder.decode([FederalPlayer].self, from: data)
                var anonymousPlayers = players.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }
                let okPlayers = players.filter { $0.firstName.isEmpty == false && $0.lastName.isEmpty == false }

                print("before anonymousPlayers.count", anonymousPlayers.count)
                FileImportManager.shared.updatePlayers(isMale: fileURL.manData, players: &anonymousPlayers)
                print("after local anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }.count)
                
                await fetchPlayersDataSequentially(for: &anonymousPlayers)

                print("after beach anonymousPlayers.count", anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }
                    .count)
                SourceFileManager.shared.exportToCSV(players: okPlayers + anonymousPlayers, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath)
                SourceFileManager.shared.exportToCSV("anonymes", players: anonymousPlayers.filter { $0.firstName.isEmpty && $0.lastName.isEmpty }, sourceFileType: fileURL.manData ? .messieurs : .dames, date: fileURL.dateFromPath)
            } catch {
                Logger.error(error)
            }
        }
    }
#endif
}

func updateCSVFile(sourceCSVURL: URL, updatedCSVURL: URL) {
    do {
        let sourceCSVContent = try String(contentsOf: sourceCSVURL, encoding: .utf8)
        var sourceCSVLines = sourceCSVContent.components(separatedBy: "\n")
        let delimiter = ";"
        
        let updatedCSVContent = try String(contentsOf: updatedCSVURL, encoding: .utf8)
        let updatedCSVLines = updatedCSVContent.components(separatedBy: "\n")
        
        // Create a dictionary of updated player data by licenseId
        var updatedPlayerDict: [String: String] = [:]
        for line in updatedCSVLines {
            let components = line.components(separatedBy: delimiter)
            if let licenseId = components.dropFirst(5).first {
                updatedPlayerDict[licenseId] = line
            }
        }
        
        // Update the source CSV lines if licenseId matches
        for (index, line) in sourceCSVLines.enumerated() {
            let components = line.components(separatedBy: delimiter)
            if let licenseId = components.dropFirst(5).first, let updatedLine = updatedPlayerDict[licenseId] {
                sourceCSVLines[index] = updatedLine
            }
        }
        
        // Write back to the file
        let finalCSVContent = sourceCSVLines.joined(separator: "\n")
        try finalCSVContent.write(to: sourceCSVURL, atomically: true, encoding: .utf8)
        print("CSV file updated successfully.")
    } catch {
        print("Error updating CSV file: \(error)")
    }
}

// Function to fetch data for a single license ID
func fetchPlayerData(for licenseID: String, idHomologation: String, sessionId: String) async throws -> [Player]? {
    guard let url = URL(string: "https://beach-padel.app.fft.fr/beachja/rechercheJoueur/licencies?idHomologation=\(idHomologation)&numeroLicence=\(licenseID)") else {
        throw URLError(.badURL)
    }

    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("application/json, text/javascript, */*; q=0.01", forHTTPHeaderField: "Accept")
    request.setValue("same-origin", forHTTPHeaderField: "Sec-Fetch-Site")
    request.setValue("fr-FR,fr;q=0.9", forHTTPHeaderField: "Accept-Language")
    request.setValue("gzip, deflate, br", forHTTPHeaderField: "Accept-Encoding")
    request.setValue("cors", forHTTPHeaderField: "Sec-Fetch-Mode")
    request.setValue("beach-padel.app.fft.fr", forHTTPHeaderField: "Host")
    request.setValue("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.6 Safari/605.1.15", forHTTPHeaderField: "User-Agent")
    request.setValue("keep-alive", forHTTPHeaderField: "Connection")
    request.setValue("https://beach-padel.app.fft.fr/beachja/competitionFiche/inscrireEquipe?identifiantHomologation=82477107", forHTTPHeaderField: "Referer")
    request.setValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")

    // Add cookies if needed (example cookie header value shown, replace with valid cookies)
    request.setValue(sessionId, forHTTPHeaderField: "Cookie")

    let (data, _) = try await URLSession.shared.data(for: request)
    let decoder = JSONDecoder()
    
    // Debug: Print raw JSON data for inspection
    if let jsonString = String(data: data, encoding: .utf8) {
        print("Raw JSON response: \(jsonString)")
    }
    
    // Decode the response
    let response = try decoder.decode(Response.self, from: data)
    let players = response.object.listeJoueurs
    
    // Cast the JSON object to [String: Any] dictionary
    return players
}

// Function to fetch data for multiple license IDs using TaskGroup
func fetchPlayersDataSequentially(for licenseIDs: inout [FederalPlayer]) async {
    var idHomologation: String = "82469282"
    if let _idHomologation = PListReader.readString(plist: "local", key: "idHomologation") {
        idHomologation = _idHomologation
    }

    var sessionId: String = ""

    if let _sessionId = PListReader.readString(plist: "local", key: "JSESSIONID") {
        sessionId = _sessionId
    }


    for licenseID in licenseIDs.filter({ $0.firstName.isEmpty && $0.lastName.isEmpty }) {
        do {
            if let playerData = try await fetchPlayerData(for: licenseID.license, idHomologation: idHomologation, sessionId: sessionId)?.first {
                licenseID.lastName = playerData.nom
                licenseID.firstName = playerData.prenom
            }
        } catch {
            print(error)
        }
    }
}


struct Player: Codable {
    let licence: Int
    let nom: String
    let prenom: String
    let sexe: String
}

struct Response: Codable {
    let object: PlayerList
}

struct PlayerList: Codable {
    let listeJoueurs: [Player]
}
