"""
Exercise 1: try/except/else/finally and custom exceptions

STEP 1: Implement `safe_divide(a, b)` — return a / b, but catch
        ZeroDivisionError and return None instead of raising.

STEP 2: Implement `parse_int_list(strings)` — try to convert every string
        in `strings` to an int. Use try/except/else/finally:
          - except ValueError: append the bad string (not the exception)
            to the module-level list BAD_INPUTS, and skip it (don't add
            it to the result).
          - else: append the successfully parsed int to the result list
            (only runs when int() did NOT raise).
          - finally: increment ATTEMPTS by 1, every single time, whether
            parsing succeeded or failed.
        Return the list of successfully parsed ints.

STEP 3: Define `InsufficientFundsError(Exception)` with an __init__ that
        takes (requested, available), calls super().__init__ with a
        message of the form f"requested {requested}, only {available}
        available", and stores both values as attributes of the same name.

STEP 4: Implement `withdraw(balance, amount)` — return balance - amount if
        amount <= balance, otherwise raise InsufficientFundsError.
"""

BAD_INPUTS = []
ATTEMPTS = 0


def safe_divide(a, b):
    # TODO: return a / b, but return None on ZeroDivisionError
    pass


def parse_int_list(strings):
    global ATTEMPTS
    result = []
    for s in strings:
        # TODO: try int(s); on ValueError record s in BAD_INPUTS; on success
        # (else clause) append to result; in finally, ATTEMPTS += 1
        pass
    return result


class InsufficientFundsError(Exception):
    def __init__(self, requested, available):
        # TODO: call super().__init__ with the formatted message, then
        # store self.requested and self.available
        pass


def withdraw(balance, amount):
    # TODO: raise InsufficientFundsError(amount, balance) if amount > balance,
    # otherwise return balance - amount
    pass


# --- self-check: do not modify below this line ---
checks = []

checks.append(("safe_divide normal case", safe_divide(10, 2), 5.0))
checks.append(("safe_divide by zero returns None", safe_divide(10, 0), None))

BAD_INPUTS.clear()
ATTEMPTS = 0
parsed = parse_int_list(["1", "two", "3", "four", "5"])
checks.append(("parse_int_list returns only valid ints", parsed, [1, 3, 5]))
checks.append(("parse_int_list records bad strings", list(BAD_INPUTS), ["two", "four"]))
checks.append(("parse_int_list counts every attempt", ATTEMPTS, 5))

try:
    withdraw(50, 100)
    err = None
except InsufficientFundsError as e:
    err = e
checks.append(("withdraw raises InsufficientFundsError", isinstance(err, InsufficientFundsError), True))
checks.append(("InsufficientFundsError stores requested", getattr(err, "requested", None), 100))
checks.append(("InsufficientFundsError stores available", getattr(err, "available", None), 50))
checks.append(("InsufficientFundsError message", str(err) if err else None, "requested 100, only 50 available"))
checks.append(("withdraw succeeds when funds sufficient", withdraw(100, 40), 60))

passed = 0
for label, actual, expected in checks:
    ok = actual == expected
    passed += ok
    print(f"{'PASS' if ok else 'FAIL'}: {label} (got {actual!r}, expected {expected!r})")

print("PASS" if passed == len(checks) else "FAIL")
