﻿"""
Zadanie 02: Definiowanie własnych wyjątków (SaldoUjemneError)

Zadanie pokazuje, jak tworzyć autorskie klasy wyjątków dziedziczące po wbudowanej
klasie Exception oraz jak przekazywać do nich dodatkowe atrybuty. Pozwala to na
bardziej precyzyjną obsługę błędów w logice biznesowej aplikacji (np. brak środków).
"""

class SaldoUjemneError(Exception):
    """
    Niestandardowy wyjątek podnoszony w sytuacji, gdy próba wypłaty
    środków przekracza dostępne saldo na koncie.
    Dziedziczy po wbudowanej klasie Exception.
    """
    
    def __init__(self, message: str, aktualne_saldo: float, kwota_wyplaty: float):
        """
        Inicjalizuje wyjątek z własnymi atrybutami.
        
        Args:
            message (str): Główny komunikat błędu.
            aktualne_saldo (float): Aktualny stan konta.
            kwota_wyplaty (float): Kwota, którą użytkownik próbował wypłacić.
        """
        # Wywołujemy konstruktor klasy bazowej (Exception), przekazując główny komunikat.
        # Jest to niezbędne, by wyjątek zachowywał się poprawnie (np. wypisywał message w Tracebacku).
        super().__init__(message)
        
        # Inicjalizujemy nasze własne, dodatkowe atrybuty biznesowe
        self.aktualne_saldo = aktualne_saldo
        self.kwota_wyplaty = kwota_wyplaty
        
        # Dodatkowo możemy wewnątrz błędu wyliczyć, ile dokładnie zabrakło pieniędzy
        self.brakujaca_kwota = kwota_wyplaty - aktualne_saldo

def wyplac(saldo: float, kwota: float) -> float:
    """
    Wykonuje wypłatę podanej kwoty z konta, jeśli saldo jest wystarczające.
    W przeciwnym wypadku rzuca wyjątek biznesowy SaldoUjemneError.
    
    Args:
        saldo (float): Aktualne saldo.
        kwota (float): Żądana kwota wypłaty.
        
    Returns:
        float: Nowe saldo po wypłacie.
        
    Raises:
        SaldoUjemneError: Jeżeli kwota wypłaty przewyższa aktualne saldo.
    """
    # Sprawdzenie warunku biznesowego - if kwota > saldo: raise ...
    if kwota > saldo:
        # Zgłaszamy NASZ własny wyjątek, przekazując mu odpowiednie dane
        raise SaldoUjemneError("Brak środków na koncie!", aktualne_saldo=saldo, kwota_wyplaty=kwota)
    
    # Jeśli wyjątek nie został rzucony, kontynuujemy działanie funkcji (wypłata się udała)
    nowe_saldo = saldo - kwota
    return nowe_saldo


if __name__ == "__main__":
    saldo_konta = 100.0
    proba_wyplaty = 500.0
    
    print(f">>> wyplac(saldo={saldo_konta}, kwota={proba_wyplaty})")
    
    try:
        # Próba wypłaty (kod ryzykowny, mogący rzucić nasz wyjątek)
        nowe = wyplac(saldo_konta, proba_wyplaty)
        print(f"Wypłata udana! Aktualne saldo: {nowe}")
        
    except SaldoUjemneError as e:
        # Przechwytujemy TYLKO nasz specyficzny błąd biznesowy.
        # Używamy aliasu "as e", by uzyskać dostęp do obiektu wyjątku i jego atrybutów.
        print(f"__main__.SaldoUjemneError: {e}")
        
        # Odczytujemy naszą dodatkową zmienną zagnieżdżoną wewnątrz wyjątku,
        # by wyświetlić bardzo czytelną sugestię dla użytkownika.
        print(f"Błąd: Brakuje Ci jeszcze {e.brakujaca_kwota} zł.")
