//
//  User.swift
//  PadelClub
//
//  Created by Laurent Morvillier on 21/02/2024.
//

import Foundation
import LeStorage

enum UserRight: Int, Codable {
    case none = 0
    case edition = 1
    case creation = 2
}

@Observable
class User: ModelObject, UserBase, Storable {
    
    static func resourceName() -> String { "users" }
    static func tokenExemptedMethods() -> [HTTPMethod] { return [.post] }

//    func deleteDependencies() throws { }
    
    public var id: String = Store.randomId()
    public var username: String
    public var email: String
    var clubs: [String] = []
    var umpireCode: String?
    var licenceId: String?
    var firstName: String
    var lastName: String
    var phone: String?
    var country: String?
    
    var summonsMessageBody : String? = nil
    var summonsMessageSignature: String? = nil
    var summonsAvailablePaymentMethods: String? = nil
    var summonsDisplayFormat: Bool = false
    var summonsDisplayEntryFee: Bool = false
    var summonsUseFullCustomMessage: Bool = false
    var matchFormatsDefaultDuration: [MatchFormat: Int]? = nil
    var bracketMatchFormatPreference: MatchFormat?
    var groupStageMatchFormatPreference: MatchFormat?
    var loserBracketMatchFormatPreference: MatchFormat?
    
    init(username: String, email: String, firstName: String, lastName: String, phone: String?, country: String?) {
        self.username = username
        self.firstName = firstName
        self.lastName = lastName
        self.email = email
        self.phone = phone
        self.country = country
    }
    
    public func uuid() throws -> UUID  {
        if let uuid = UUID(uuidString: self.id) {
            return uuid
        }
        throw UUIDError.cantConvertString(string: self.id)
    }
    
    func currentPlayerData() -> ImportedPlayer? {
        guard let licenceId else { return nil }
        let federalContext = PersistenceController.shared.localContainer.viewContext
        let fetchRequest = ImportedPlayer.fetchRequest()
        let predicate = NSPredicate(format: "license == %@", licenceId)
        fetchRequest.predicate = predicate
        return try? federalContext.fetch(fetchRequest).first
    }
    
    func defaultSignature() -> String {
        return "Sportivement,\n\(firstName) \(lastName), votre JAP."
    }
    
    func hasTenupClubs() -> Bool {
        self.clubsObjects().filter({ $0.code != nil }).isEmpty == false
    }
    
    func hasFavoriteClubsAndCreatedClubs() -> Bool {
        clubsObjects(includeCreated: true).isEmpty == false
    }
    
    func setUserClub(_ userClub: Club) {
        self.clubs.insert(userClub.id, at: 0)
    }
    
    func clubsObjects(includeCreated: Bool = false) -> [Club] {
        return Store.main.filter(isIncluded: { (includeCreated && $0.creator == id) || clubs.contains($0.id) })
    }
    
    func createdClubsObjectsNotFavorite() -> [Club] {
        return Store.main.filter(isIncluded: { ($0.creator == id) && clubs.contains($0.id) == false })
    }

    func saveMatchFormatsDefaultDuration(_ matchFormat: MatchFormat, estimatedDuration: Int) {
        if estimatedDuration == matchFormat.defaultEstimatedDuration {
            matchFormatsDefaultDuration?.removeValue(forKey: matchFormat)
        } else {
            matchFormatsDefaultDuration = matchFormatsDefaultDuration ?? [MatchFormat: Int]()
            matchFormatsDefaultDuration?[matchFormat] = estimatedDuration
        }
    }
    
    enum CodingKeys: String, CodingKey {
        case _id = "id"
        case _username = "username"
        case _email = "email"
        case _clubs = "clubs"
        case _umpireCode = "umpireCode"
        case _licenceId = "licenceId"
        case _firstName = "firstName"
        case _lastName = "lastName"
        case _phone = "phone"
        case _country = "country"
        case _summonsMessageBody = "summonsMessageBody"
        case _summonsMessageSignature = "summonsMessageSignature"
        case _summonsAvailablePaymentMethods = "summonsAvailablePaymentMethods"
        case _summonsDisplayFormat = "summonsDisplayFormat"
        case _summonsDisplayEntryFee = "summonsDisplayEntryFee"
        case _summonsUseFullCustomMessage = "summonsUseFullCustomMessage"
        case _matchFormatsDefaultDuration = "matchFormatsDefaultDuration"
        case _bracketMatchFormatPreference = "bracketMatchFormatPreference"
        case _groupStageMatchFormatPreference = "groupStageMatchFormatPreference"
        case _loserBracketMatchFormatPreference = "loserBracketMatchFormatPreference"

    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        
        try container.encode(id, forKey: ._id)
        try container.encode(username, forKey: ._username)
        try container.encode(email, forKey: ._email)
        try container.encode(clubs, forKey: ._clubs)
        
        if let umpireCode = umpireCode {
            try container.encode(umpireCode, forKey: ._umpireCode)
        } else {
            try container.encodeNil(forKey: ._umpireCode)
        }
        
        if let licenceId = licenceId {
            try container.encode(licenceId, forKey: ._licenceId)
        } else {
            try container.encodeNil(forKey: ._licenceId)
        }
        
        try container.encode(firstName, forKey: ._firstName)
        try container.encode(lastName, forKey: ._lastName)
        
        if let phone = phone {
            try container.encode(phone, forKey: ._phone)
        } else {
            try container.encodeNil(forKey: ._phone)
        }
        
        if let country = country {
            try container.encode(country, forKey: ._country)
        } else {
            try container.encodeNil(forKey: ._country)
        }
        
        if let summonsMessageBody = summonsMessageBody {
            try container.encode(summonsMessageBody, forKey: ._summonsMessageBody)
        } else {
            try container.encodeNil(forKey: ._summonsMessageBody)
        }
        
        if let summonsMessageSignature = summonsMessageSignature {
            try container.encode(summonsMessageSignature, forKey: ._summonsMessageSignature)
        } else {
            try container.encodeNil(forKey: ._summonsMessageSignature)
        }
        
        if let summonsAvailablePaymentMethods = summonsAvailablePaymentMethods {
            try container.encode(summonsAvailablePaymentMethods, forKey: ._summonsAvailablePaymentMethods)
        } else {
            try container.encodeNil(forKey: ._summonsAvailablePaymentMethods)
        }
        
        try container.encode(summonsDisplayFormat, forKey: ._summonsDisplayFormat)
        try container.encode(summonsDisplayEntryFee, forKey: ._summonsDisplayEntryFee)
        try container.encode(summonsUseFullCustomMessage, forKey: ._summonsUseFullCustomMessage)
        
        if let matchFormatsDefaultDuration = matchFormatsDefaultDuration {
            try container.encode(matchFormatsDefaultDuration, forKey: ._matchFormatsDefaultDuration)
        } else {
            try container.encodeNil(forKey: ._matchFormatsDefaultDuration)
        }
        
        if let bracketMatchFormatPreference = bracketMatchFormatPreference {
            try container.encode(bracketMatchFormatPreference, forKey: ._bracketMatchFormatPreference)
        } else {
            try container.encodeNil(forKey: ._bracketMatchFormatPreference)
        }
        
        if let groupStageMatchFormatPreference = groupStageMatchFormatPreference {
            try container.encode(groupStageMatchFormatPreference, forKey: ._groupStageMatchFormatPreference)
        } else {
            try container.encodeNil(forKey: ._groupStageMatchFormatPreference)
        }
        
        if let loserBracketMatchFormatPreference = loserBracketMatchFormatPreference {
            try container.encode(loserBracketMatchFormatPreference, forKey: ._loserBracketMatchFormatPreference)
        } else {
            try container.encodeNil(forKey: ._loserBracketMatchFormatPreference)
        }
    }

    static func placeHolder() -> User {
        return User(username: "", email: "", firstName: "", lastName: "", phone: nil, country: nil)
    }

}

class UserCreationForm: User, UserPasswordBase {
    
    init(user: User, username: String, password: String, firstName: String, lastName: String, email: String, phone: String?, country: String?) {
        self.password = password
        super.init(username: username, email: email, firstName: firstName, lastName: lastName, phone: phone, country: country)
        
        self.summonsMessageBody = user.summonsMessageBody
        self.summonsMessageSignature = user.summonsMessageSignature
        self.summonsAvailablePaymentMethods = user.summonsAvailablePaymentMethods
        self.summonsDisplayFormat = user.summonsDisplayFormat
        self.summonsDisplayEntryFee = user.summonsDisplayEntryFee
        self.summonsUseFullCustomMessage = user.summonsUseFullCustomMessage
        self.matchFormatsDefaultDuration = user.matchFormatsDefaultDuration
        self.bracketMatchFormatPreference = user.bracketMatchFormatPreference
        self.groupStageMatchFormatPreference = user.groupStageMatchFormatPreference
        self.loserBracketMatchFormatPreference = user.loserBracketMatchFormatPreference
        
    }
    
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
    
    public var password: String
    
    private enum CodingKeys: String, CodingKey {
        case password
    }

    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.password, forKey: .password)
    }
}
