"""
Exercise 1: a plain decorator that preserves metadata

STEP 1: Implement `shout(func)` — a decorator that returns a wrapper which
        calls `func` with whatever arguments it was given and returns the
        result uppercased. It must accept ANY signature (*args, **kwargs).

STEP 2: Use `functools.wraps` so the decorated function keeps the ORIGINAL
        function's __name__ and __doc__, and so `__wrapped__` points at the
        original undecorated function.

STEP 3: Implement `call_log(func)` — a decorator that appends the string
        f"{func.__name__} called" to the module-level list CALLS every time
        the decorated function runs, then returns the result unchanged.

Do not change the decorated functions at the bottom; only fill in the stubs.
"""

import functools

CALLS = []


def shout(func):
    # TODO: return a wrapper function decorated with @functools.wraps(func)
    # that calls func(*args, **kwargs) and returns the result .upper()
    return func


def call_log(func):
    # TODO: return a wrapper (also using @functools.wraps) that appends
    # f"{func.__name__} called" to CALLS, then returns func(*args, **kwargs)
    return func


@shout
def greet(name):
    """Say hello to someone."""
    return f"hi {name}"


@call_log
def add(a, b=0):
    """Add two numbers."""
    return a + b


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

checks.append(("shout wraps return value", greet("ana"), "HI ANA"))
checks.append(("shout preserves __name__", greet.__name__, "greet"))
checks.append(("shout preserves __doc__", greet.__doc__, "Say hello to someone."))
checks.append(("shout sets __wrapped__", getattr(greet, "__wrapped__", None) is not None, True))
checks.append(("__wrapped__ bypasses decorator", greet.__wrapped__("ana") if hasattr(greet, "__wrapped__") else None, "hi ana"))

CALLS.clear()
checks.append(("call_log passes kwargs through", add(2, b=3), 5))
checks.append(("call_log passes positionals through", add(4), 4))
checks.append(("call_log recorded both calls", list(CALLS), ["add called", "add called"]))
checks.append(("call_log preserves __name__", add.__name__, "add"))

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")
