from abc import ABC, abstractmethod
from typing import Final


class Personale :
    def __init__ (self, nome:str, cognome:str, matricola:int) :
        self._nome = nome
        self._cognome = cognome
        self._matricola:Final[int] = matricola
    
    @property
    def nome (self) -> str :
        return self._nome
    
    @property
    def cognome (self) -> str :
        return self._cognome
    
    @property
    def matricola (self) -> int :
        return self._matricola
    
    def __eq__ (self, o:object):
        if not isinstance(o, Personale) : return False
        return o.matricola==self.matricola
    
    def __hash__(self) -> int:
        return hash (self.matricola)

    def __str__(self) -> str :
        return f"{self.nome} {self.cognome}; matricola {self.matricola}"
    
class ProdottoOrdinato :
    def __init__ (self, descrizione:str, costo_unitario:float, quantità:int) :
        self._descrizione = descrizione
        self._costo_unitario = costo_unitario
        self._quantità = quantità
    
    @property
    def descrizione (self) -> str :
        return self._descrizione
    
    @property
    def costo_unitario (self) -> float :
        return self._costo_unitario
    
    @property
    def quantità (self) -> int :
        return self._quantità
    
    def __repr__ (self) -> str:
        #return self.__str__()
        return str(self)

    def __str__(self) -> str :
        return f"({self.descrizione}; costo: {self.costo_unitario}; quantità {self.quantità})"


class Ordine (ABC) :
    def __init__ (self, cameriere:Personale) :
        self._cameriere = cameriere
        self._comanda:Final[list[ProdottoOrdinato]] = [] 
    
    @property
    def cameriere (self) -> Personale :
        return self._cameriere
    
    @abstractmethod
    def get_totale (self) -> float :
        ris:float =0
        for i in self._comanda:
            ris += i.quantità * i.costo_unitario
        return ris
        
    
    #metodo che aggiunge un prodotto ordinato alla comanda
    def add_prodotto (self, p:ProdottoOrdinato):
        self._comanda.append(p)


    def __str__(self) -> str :
        return f"cameriere: {self.cameriere}; comanda:{self._comanda}"


class OrdineTavolo (Ordine) :
    def __init__ (self, cameriere:Personale, tavolo:str, coperti:int) :
        super().__init__(cameriere)
        self._tavolo = tavolo
        self._coperti:Final[int] = coperti 

    @property
    def tavolo (self) -> str :
        return self._tavolo
    
    @property
    def coperti (self) -> int :
        return self._coperti
    
    def get_totale (self) -> float :
        return super().get_totale() + 1.5*self._coperti
    
    def __str__(self) -> str :
        return f"Ordine Tavolo {self.tavolo} con {self.coperti} coperti: " + super().__str__()
    
class OrdineAsporto (Ordine) :
    def __init__ (self, cameriere:Personale, nominativo:str) :
        super().__init__(cameriere)
        self._nominativo = nominativo

    @property
    def nominativo (self) -> str :
        return self._nominativo
    
    def get_totale (self) -> float :
        return super().get_totale()
    
    def __str__(self) -> str :
        return f"Ordine Asporto a nome {self.nominativo}: " + super().__str__()

class FastFood :
    def __init__ (self) :
        self._persone:Final[list[Personale]] = []
        self._ordini:Final[list[Ordine]] = [] 
    
    #metodo che aggiunge un cameriere
    def add_cameriere (self, p:Personale):
        self._persone.append(p)


    #metodo che aggiunge un ordine
    def add_ordine (self, o:Ordine):
        self._ordini.append(o)


    def totale_asporto (self) -> float :
        ris:float = 0
        for o in self._ordini:
            if isinstance(o, OrdineAsporto) :
                ris += o.get_totale()
        return ris

    def get_migliore (self) -> Personale | None :
        conteggio:dict[Personale, float]={}
        ris:Personale|None=None
        for cameriere in self._persone :
            conteggio[cameriere] = 0
        for o in self._ordini :
            if isinstance(o, OrdineTavolo) :
                if o.cameriere in conteggio :
                    conteggio[o.cameriere] += o.get_totale()
                    if ris is None or conteggio[o.cameriere] > conteggio[ris] :
                        ris = o.cameriere
        return ris

            




def test() :
    c1 = Personale("Pippo", "Rossi", 100200)
    c2 = Personale("Pluto", "Rossi", 123456)

    o1 = OrdineAsporto (c1, "Luca")
    o2 = OrdineAsporto (c2, "Simona")
    o3 = OrdineTavolo(c1, "A1", 3)
    o4 = OrdineTavolo(c2, "A12", 5)
    o5 = OrdineTavolo(c1, "A22", 2)
    o6 = OrdineTavolo(c2, "A32", 15)

    f = FastFood()
    f.add_cameriere(c1)
    f.add_cameriere(c2)
    f.add_ordine(o1)
    f.add_ordine(o2)
    f.add_ordine(o3)
    f.add_ordine(o4)
    f.add_ordine(o5)
    f.add_ordine(o6)


    o1.add_prodotto(ProdottoOrdinato("Patatine", 2.5, 3))
    o1.add_prodotto(ProdottoOrdinato("Hamburger", 5, 2))
    
    o2.add_prodotto(ProdottoOrdinato("Patatine", 2.5, 3))
    
    o3.add_prodotto(ProdottoOrdinato("Patatine", 2.5, 30))
    
    o4.add_prodotto(ProdottoOrdinato("Hamburger", 5, 20))
    o4.add_prodotto(ProdottoOrdinato("Patatine", 2.5, 3))
    
    o5.add_prodotto(ProdottoOrdinato("Hamburger", 5, 1))
    o5.add_prodotto(ProdottoOrdinato("Patatine", 2.5, 1))
    

    o6.add_prodotto(ProdottoOrdinato("Birra", 5, 10))
    o6.add_prodotto(ProdottoOrdinato("Coca-cola", 3.5, 5))
    
    print (o1)
    print (f.totale_asporto())
    print (f.get_migliore())




test()

