from datetime import date
import json

class Service:
    """
    Application déployée sur un serveur.
    """
    def __init__(self, name: str, port: str | int, protocol: str, is_critical: bool):
        self.name        = name
        self.port        = int(port)
        self.protocol    = protocol
        self.is_critical = is_critical

    def __eq__(self, other: "Service"):
        return self.name == other.name and self.port == other.port

    def serialized_services(self):
        """Converti la classe \"Services\" en dictionnaire JSONable"""
        return {
            "Name": self.name,
            "Port": self.port,
            "Protocol": self.protocol,
            "Is_Critical": self.is_critical,
            }

    @classmethod
    def deserialized_services(cls, dictionnary):
        """Lis un JSON pour le retranscrire en classe \"Services\""""
        return cls(
            name= dictionnary["Name"],
            port= dictionnary["Port"],
            protocol= dictionnary["Protocol"],
            is_critical= dictionnary["Is_Critical"],
            )

class Serveur:
    """
    Serveur, classe maîtresse. NE PAS UTILISER DIRECTEMENT.
    Préférez les classes héritées :
    - ServeurPhysique
    - ServeurVirtuel
    """
    def __init__(self, name: str, ip_address: str, os_name: str, commission_date: date):
        self.name            = name
        self.ip_address     = ip_address
        self.os_name         = os_name
        self.commission_date = commission_date
        self.services        = self.load_services()
    
    def __str__(self) -> str:
        return f"\
            [Serveur]                > {self.name}\n\
            [Addresse IP]            > {self.ip_address}\n\
            [Système d'Exploitation] > {self.os_name}\n\
            [Date de mise en prod]   > {self.commission_date}"

    def __len__(self) -> int:
        return len(self.services)

    def list_services(self):
        return self.services
    
    def add_service(self, service: Service):
        """Ajout d'un service à la liste des services disponibles"""
        if service in self.services:
            return f"Le service [{service.name.capitalize()}] est déjà présent."
        self.services.append(service)
        self.save_services()
        return f"{service.name.capitalize()} a été ajouté."
    
    def remove_service(self, service_str: str):
        """Retire un service de la liste des services disponibles"""
        for service in self.services:
            if service.name.lower() == service_str.lower()\
            or str(service.port) == service_str.lower()\
            or service.protocol.lower() == service_str.lower():
                self.services.remove(service)
                self.save_services()
                return f"{service.name.capitalize} a été retiré."
        return f"{service.name.capitalize()} n'est pas dans la liste."
    
    def serialized_servers(self):
        """Converti la classe \"Services\" en dictionnaire JSONable"""
        return {
            "Name": self.name,
            "IP_Address": self.ip_address,
            "OS_Name": self.os_name,
            "Commission_Date": self.commission_date,
            }

    @classmethod
    def deserialized_servers(cls, dictionnary):
        """Lis un JSON pour le retranscrire en classe \"Services\""""
        return cls(
            name= dictionnary["Name"],
            ip_address= dictionnary["IP_Address"],
            os_name= dictionnary["OS_Name"],
            commission_date= dictionnary["Commission_Date"],
            )

    def save_services(self):
        """
        Inscris dans un fichier JSON le contenu des services disponibles
        """
        services_dict = [services.serialized_services() for services in self.services]
        with open("services.json", "w", encoding= "utf-8") as json_file:
            json.dump(services_dict, json_file, indent= 4)

    def load_services(self) -> list[Service]:
        """
        Récupère le contenu d'un fichier JSON contenant les services
        """
        with open("services.json", encoding= "utf-8") as json_file:
            return json.load(json_file, object_hook= Service.deserialized_services)
        
class ServeurPhysique(Serveur):
    def __init__(self, name: str, ip_address: str, os_name: str, commission_date: date, rack: str, electric_cons: int, warranty: bool):
        super().__init__(name, ip_address, os_name, commission_date)
        self.rack          = rack
        self.electric_cons = electric_cons
        warranty_bools     = ("Non", "Oui")
        self.warranty      = warranty_bools[warranty]
    
    def __str__(self) -> str:
        return f"\
            {super().__str__()}\n\
            [Châssis]                > {self.rack}\n\
            [Consommation en kWh]    > {self.electric_cons}\n\
            [Sous garantie]          > {self.warranty}"
    
    def __len__(self) -> int:
        return super().__len__()

class ServeurVirtuel(Serveur):
    def __init__(self, name: str, ip_address: str, os_name: str, commission_date: date, hypervisor: str, vcpus: int, giga_ram: float):
        super().__init__(name, ip_address, os_name, commission_date)
        self.hypervisor = hypervisor
        self.vcpus      = vcpus
        self.giga_ram   = giga_ram

    def __str__(self) -> str:
        return f"\
            {super().__str__()}\n\
            [Hyperviseur]            > {self.hypervisor}\n\
            [Coeurs virtuels]        > {self.vcpus}\n\
            [Mémoire vive (Go)]      > {self.giga_ram}"
    
    def __len__(self) -> int:
        return super().__len__()

class Maintenance:
    def __init__(self, id: int, commission_date: date, type: str, affected_technician: "Technicien", associated_server: "ServeurPhysique | ServeurVirtuel"):
        self.id                  = id
        self.commission_date     = commission_date
        self.type                = type
        self.affected_technician = affected_technician
        self.associated_server   = associated_server

    def __str__(self) -> str:
        return f"\
            [ID]                   > {self.id}\n\
            [Serveur associé]      > {self.associated_server}\n\
            [Date de mise en prod] > {self.commission_date}\n\
            [Type de maintenance]  > {self.type}\n\
            [Technicien affecté]   > {self.affected_technician}"

    # J'ai le sentiment que ce n'est PAS ce qui est demandé. Mais il est 16h25 et je n'ai plus d'idée sur comment le faire
    def link_to_server(self, server: ServeurPhysique | ServeurVirtuel):
        return f"La maintenance est liée au serveur {server.name.upper()}, addresse IP {server.ip_address}."

    def serialized_maintenances(self):
        """Converti la classe \"Maintenance\" en dictionnaire JSONable"""
        return {
            "ID": self.id,
            "Associated_Server": self.associated_server,
            "Commission_Date": self.commission_date,
            "Type": self.type,
            "Affected_Technician": self.affected_technician,
            }

    @classmethod
    def deserialized_maintenances(cls, dictionnary):
        """Lis un JSON pour le retranscrire en classe \"Maintenance\""""
        return cls(
            id= dictionnary["ID"],
            associated_server= dictionnary["Associated_Server"],
            commission_date= dictionnary["Commission_Date"],
            type= dictionnary["Type"],
            affected_technician= dictionnary["Affected_Technician"],
            )

class Technicien:
    def __init__(self, fullname: str, speciality: str, id: int):
        self.name         = fullname
        self.speciality   = speciality
        self.id           = id
        self.maintenances = self.load_maintenances()
    
    def list_affected_jobs(self):
        return self.maintenances
    
    def affect_maintenance(self):
        pass

    def save_maintenances(self):
        """
        Inscris dans un fichier JSON le contenu des maintenances disponibles
        """
        maintenances_dict = [maintenances.serialized_maintenances() for maintenances in self.maintenances]
        with open("maintenances.json", "w", encoding= "utf-8") as json_file:
            json.dump(maintenances_dict, json_file, indent= 4)

    def load_maintenances(self) -> list[Maintenance]:
        """
        Récupère le contenu d'un fichier JSON contenant les maintenances
        """
        with open("maintenances.json", encoding= "utf-8") as json_file:
            return json.load(json_file, object_hook= Maintenance.deserialized_maintenances)
        
class Datacenter:
    def __init__(self, name: str, location: str, id: int):
        self.name     = name
        self.location = location
        self.id       = id
        self.servers  = self.load_servers()

    def list_servers(self):
        return self.servers
    
    def list_critical_services(self):
        for server in self.servers:
            server.list_services()
        # Là j'ai cherché à récupérer un genre de 'if server.list_services().is_critical'
        # pour avoir le "True/False" et renvoyer le résultat en cas de "True" mais j'ai craqué

    def list_services_number(self):
        services = 0
        for server in self.servers:
            services += len(server)
        return services

    def save_servers(self):
        """
        Inscris dans un fichier JSON le contenu des serveurs disponibles
        """
        servers_dict = [servers.serialized_servers() for servers in self.servers]
        with open("servers.json", "w", encoding= "utf-8") as json_file:
            json.dump(servers_dict, json_file, indent= 4)

    def load_servers(self) -> list[Serveur]:
        """
        Récupère le contenu d'un fichier JSON contenant les serveurs
        """
        with open("servers.json", encoding= "utf-8") as json_file:
            return json.load(json_file, object_hook= Serveur.deserialized_servers)