"""
Exercise 2: parameterized decorators and stacking order

STEP 1: Implement `repeat(times)` — a THREE-layer parameterized decorator.
        The decorated function is called `times` times; the wrapper returns
        the LIST of all results, in call order.

STEP 2: Implement `tag(name)` — a parameterized decorator whose wrapper wraps
        the (string) return value as f"<{name}>{result}</{name}>".

STEP 3: Implement `trace(label)` — a parameterized decorator that appends
        `label` to the module-level list ORDER when the wrapper is ENTERED
        (before delegating), so stacking order becomes observable.

STEP 4: Read the self-check's expectations for `styled()` carefully. Two
        `@tag(...)` decorators are stacked. Application is bottom-up, so the
        CALL order is outside-in: the top decorator's wrapper runs first and
        therefore produces the OUTERMOST tag in the final string.
"""

import functools

ORDER = []


def repeat(times):
    # TODO: layer 1 takes `times`; return a decorator that takes `func`;
    # that decorator returns a @functools.wraps(func) wrapper which calls
    # func(*args, **kwargs) `times` times and returns the list of results.
    def decorator(func):
        return func
    return decorator


def tag(name):
    # TODO: same three-layer shape; wrapper returns
    # f"<{name}>{func(*args, **kwargs)}</{name}>"
    def decorator(func):
        return func
    return decorator


def trace(label):
    # TODO: three layers again; the wrapper must append `label` to ORDER
    # BEFORE calling func, then return func(*args, **kwargs) unchanged.
    def decorator(func):
        return func
    return decorator


counter = {"n": 0}


@repeat(times=3)
def tick():
    counter["n"] += 1
    return counter["n"]


@tag("b")
@tag("i")
def styled():
    return "hi"


@trace("outer")
@trace("inner")
def work():
    return "done"


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

counter["n"] = 0
checks.append(("repeat returns every result in order", tick(), [1, 2, 3]))
checks.append(("repeat really called it 3 times", counter["n"], 3))
checks.append(("repeat preserves __name__", tick.__name__, "tick"))

checks.append(("stacked tags nest outside-in", styled(), "<b><i>hi</i></b>"))

ORDER.clear()
checks.append(("trace passes result through", work(), "done"))
checks.append(("top decorator's wrapper runs first", list(ORDER), ["outer", "inner"]))

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