//
//  NetworkFederalService.swift
//  PadelClub
//
//  Created by Razmig Sarkissian on 21/03/2024.
//

import Foundation
import CoreLocation

class NetworkFederalService {
    struct HttpCommand: Decodable {
        let command: String
        let results: HttpResults?
        let method : String?
        let selector : String?
        let data: String?
    }

    struct HttpResults: Decodable {
        let nb_results : Int
        let items: [FederalTournament]?
    }

    static let shared: NetworkFederalService = NetworkFederalService()
    var formId = ""
    
    var tenupJsonDecoder: JSONDecoder = {
        let decoder = JSONDecoder()
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS"
        decoder.dateDecodingStrategy = .formatted(dateFormatter)
        return decoder
    }()
    
    func runTenupTask<T:Decodable>(request: URLRequest) async throws -> T {
        let task = try await URLSession.shared.data(for: request)
        if request.httpMethod == "PUT" {
            print("tried PUT: \(request.url!)")
            if let urlResponse = task.1 as? HTTPURLResponse {
                print(urlResponse.statusCode)
            }
        }
        return try tenupJsonDecoder.decode(T.self, from: task.0)
    }
    
    func federalClubs(country: String = "fr", city: String, radius: Double, location: CLLocation? = nil) async throws -> FederalClubResponse {
        
        /*
         {
             "geocoding[country]": "fr",
             "geocoding[ville]": "Cayenne, 973, Guyane",
             "geocoding[rayon]": "15",
             "geocoding[userPosition][lng]": "-52.311583",
             "geocoding[userPosition][lat]": "4.925248",
             "geocoding[userPosition][showDistance]": "false",
             "pratiqueOption[0]": "PADEL",
             "nombreResultat": "6",
             "diplomeEtatOption": "false",
             "galaxieOption": "false",
             "fauteuilOption": "false",
             "tennisSanteOption": "false"
         }
         */
        
        var parameters = "geocoding[country]=\(country)&geocoding[ville]=\(city)&geocoding[rayon]=\(Int(radius))&pratiqueOption[0]=Padel"
        
        if let location {
            parameters = parameters + "&geocoding[userPosition][lat]=\(location.coordinate.latitude.formatted(.number.locale(Locale(identifier: "us"))))&geocoding[userPosition][lng]=\(location.coordinate.longitude.formatted(.number.locale(Locale(identifier: "us"))))&geocoding[userPosition][showDistance]=true"
        }
        //"geocoding%5Bcountry%5D=fr&geocoding%5Bville%5D=13%20Avenue%20Emile%20Bodin%2013260%20Cassis&geocoding%5Brayon%5D=15&geocoding%5BuserPosition%5D%5Blat%5D=43.22278594081477&geocoding%5BuserPosition%5D%5Blng%5D=5.556953900769194&geocoding%5BuserPosition%5D%5BshowDistance%5D=true&nombreResultat=0&diplomeEtatOption=false&galaxieOption=false&fauteuilOption=false&tennisSanteOption=false"
        let postData =  parameters.data(using: .utf8)

        var request = URLRequest(url: URL(string: "https://tenup.fft.fr/recherche/clubs/ajax")!,timeoutInterval: Double.infinity)
        request.addValue("application/json, text/plain, */*", forHTTPHeaderField: "Accept")
        request.addValue("fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3", forHTTPHeaderField: "Accept-Language")
        request.addValue("gzip, deflate, br", forHTTPHeaderField: "Accept-Encoding")
        request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        request.addValue("https://tenup.fft.fr", forHTTPHeaderField: "Origin")
        request.addValue("keep-alive", forHTTPHeaderField: "Connection")
        request.addValue("https://tenup.fft.fr/recherche/clubs/list", forHTTPHeaderField: "Referer")
    //        request.addValue("a20ba3b563e5ce7ad731c2c1076b217f=a2de91fbefddf75ea4aa86297ed09bd5; visid_incap_2712217=TZgb6G1zTsiPtpJ4cCmOErtj8GQAAAAAQUIPAAAAAAC5nrgD+rm7QWCdUN5I8Y6T; nlbi_2712217=Ug01X5TrSizGkQw5qBb2twAAAAAOvBNMkIHMeRAJGDiOaFxs; incap_ses_391_2712217=E60LNJzW7B+BjW0qWx1tBbtj8GQAAAAAlw5keZVI9C7egwKQblAHeQ==; TCPID=1238411561211442193459; TCID=; incap_ses_391_2712217=MzHHL4jK9k4gpmkqWx1tBT9i8GQAAAAA1k2Eroyuow6SC5Zmf1WtVA==; visid_incap_2712217=lVlg9romTq6I9k4sVklsgr9F72QAAAAAQUIPAAAAAADw7ISp7aFXSsqidxqlj3Df", forHTTPHeaderField: "Cookie")
        request.addValue("empty", forHTTPHeaderField: "Sec-Fetch-Dest")
        request.addValue("cors", forHTTPHeaderField: "Sec-Fetch-Mode")
        request.addValue("same-origin", forHTTPHeaderField: "Sec-Fetch-Site")
        request.addValue("trailers", forHTTPHeaderField: "TE")

        request.httpMethod = "POST"
        request.httpBody = postData

        return try await runTenupTask(request: request)
    }

    
    func getClubFederalTournaments(page: Int, tournaments: [FederalTournament], club: String, codeClub: String, startDate: Date? = nil, endDate: Date? = nil) async throws -> [FederalTournament] {
        
        if formId.isEmpty {
            do {
                try await getNewBuildForm()
            } catch {
                print("getClubFederalTournaments", error)
            }
        }
        
        var dateComponent = ""
        if let startDate, let endDate {
            dateComponent = "&date[start]=\(startDate.twoDigitsYearFormatted)&date[end]=\(endDate.endOfMonth.twoDigitsYearFormatted)"
        } else if let startDate {
            dateComponent = "&date[start]=\(startDate.twoDigitsYearFormatted)&date[end]=\(Calendar.current.date(byAdding: .month, value: 3, to: startDate)!.endOfMonth.twoDigitsYearFormatted)"
        }
        
        let parameters = """
recherche_type=club&club[autocomplete][value_container][value_field]=\(codeClub.replaceCharactersFromSet(characterSet: .whitespaces))&club[autocomplete][value_container][label_field]=\(club.replaceCharactersFromSet(characterSet: .whitespaces, replacementString: "+"))&pratique=PADEL\(dateComponent)&page=\(page)&sort=dateDebut+asc&form_build_id=\(formId)&form_id=recherche_tournois_form&_triggering_element_name=submit_page&_triggering_element_value=Submit+page
"""
        let postData = parameters.data(using: .utf8)

        var request = URLRequest(url: URL(string: "https://tenup.fft.fr/system/ajax")!,timeoutInterval: Double.infinity)
        request.addValue("application/json, text/javascript, */*; q=0.01", forHTTPHeaderField: "Accept")
        request.addValue("fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3", forHTTPHeaderField: "Accept-Language")
        request.addValue("gzip, deflate, br", forHTTPHeaderField: "Accept-Encoding")
        request.addValue("application/x-www-form-urlencoded; charset=UTF-8", forHTTPHeaderField: "Content-Type")
        request.addValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")
        request.addValue("https://tenup.fft.fr", forHTTPHeaderField: "Origin")
        request.addValue("keep-alive", forHTTPHeaderField: "Connection")
        request.addValue("https://tenup.fft.fr/recherche/tournois", forHTTPHeaderField: "Referer")
        request.addValue("empty", forHTTPHeaderField: "Sec-Fetch-Dest")
        request.addValue("cors", forHTTPHeaderField: "Sec-Fetch-Mode")
        request.addValue("same-origin", forHTTPHeaderField: "Sec-Fetch-Site")

        request.httpMethod = "POST"
        request.httpBody = postData

        let commands : [HttpCommand] = try await runTenupTask(request: request)
        if commands.anySatisfy({ $0.command == "alert" }) { 
            throw NetworkManagerError.maintenance
        }
        let resultCommand = commands.first(where: { $0.results != nil })
        if let gatheredTournaments = resultCommand?.results?.items {
            var finalTournaments = tournaments + gatheredTournaments
            if let count = resultCommand?.results?.nb_results {
                if finalTournaments.count < count {
                    let newTournaments = try await getClubFederalTournaments(page: page+1, tournaments: finalTournaments, club: club, codeClub: codeClub)
                    finalTournaments = finalTournaments + newTournaments
                }
            }
            
            return finalTournaments
        }

//        do {
//        } catch {
//            print("getClubFederalTournaments", error)
//        }
//        
        return []
    }

    func getNewBuildForm() async throws {
        var request = URLRequest(url: URL(string: "https://tenup.fft.fr/recherche/tournois")!,timeoutInterval: Double.infinity)
        request.addValue("application/json, text/javascript, */*; q=0.01", forHTTPHeaderField: "Accept")
        request.addValue("fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3", forHTTPHeaderField: "Accept-Language")
        request.addValue("gzip, deflate, br", forHTTPHeaderField: "Accept-Encoding")
        request.addValue("application/x-www-form-urlencoded; charset=UTF-8", forHTTPHeaderField: "Content-Type")
        request.addValue("XMLHttpRequest", forHTTPHeaderField: "X-Requested-With")
        request.addValue("https://tenup.fft.fr", forHTTPHeaderField: "Origin")
        request.addValue("keep-alive", forHTTPHeaderField: "Connection")
        request.addValue("https://tenup.fft.fr/recherche/tournois", forHTTPHeaderField: "Referer")
        request.addValue("empty", forHTTPHeaderField: "Sec-Fetch-Dest")
        request.addValue("cors", forHTTPHeaderField: "Sec-Fetch-Mode")
        request.addValue("same-origin", forHTTPHeaderField: "Sec-Fetch-Site")
        request.addValue("trailers", forHTTPHeaderField: "TE")

        request.httpMethod = "GET"
        let task = try await URLSession.shared.data(for: request)

        if let stringData = String(data: task.0, encoding: .utf8) {
            let stringDataFolded = stringData.replaceCharactersFromSet(characterSet: .whitespacesAndNewlines)

            let prefix = "form_build_id\"value=\"form-"
            var finalData = ""
            if let lab = stringDataFolded.matches(of: try! Regex("\(prefix)")).last {
                finalData = String(stringDataFolded[lab.range.upperBound...])
            }
            
            let suffix = "\"/><inputtype=\"hidden\"name=\"form_id\"value=\"recherche_tournois_form"
            if let suff = finalData.firstMatch(of: try! Regex("\(suffix)")) {
                finalData = String(finalData[..<suff.range.lowerBound])
            }
            print(finalData)
            
            formId = "form-\(finalData)"

        } else {
            print("no data found in html")
        }
    }
}
