"""
Lesson 25 — Exercise 5: Interface Debugging Between Functions
==============================================================
Goal: Find a bug that ISN'T inside either function — it lives at the
boundary where one function hands data to another.

This is one of the most useful real-world skills. Both functions look
fine. You only see the bug when you trace what's PASSED between them.
"""


def find_book_id(title, catalog):
    """
    Look up a book by title.

    Args:
        title (str): the book's title (case-insensitive)
        catalog (dict): maps title → integer id

    Returns:
        the matching book id, or None if not found
    """
    for catalog_title, book_id in catalog.items():
        if catalog_title.lower() == title.lower():
            return str(book_id)   # ← look here closely
    return None


def calculate_late_fee(book_id, fee_per_day, days_late):
    """
    Calculate the fee for a late return.

    Contract: book_id MUST be an int. We use it as a key elsewhere.
    """
    if not isinstance(book_id, int):
        raise TypeError(f"Expected int book_id, got {type(book_id).__name__}")
    return fee_per_day * days_late


def process_late_return(title, days_late, catalog, fee_per_day):
    """Top-level: look up the book, then charge the fee."""
    book_id = find_book_id(title, catalog)
    fee = calculate_late_fee(book_id, fee_per_day, days_late)
    return fee


# ═══════════════════════════════════════════════════════════════
# TASK A — RUN IT AND READ THE ERROR
# ═══════════════════════════════════════════════════════════════
# The block at the bottom of this file will crash.
# Run it. Read the error message carefully.
# Don't fix anything yet.

# ═══════════════════════════════════════════════════════════════
# TASK B — TRACE THE INTERFACE WITH A BREAKPOINT
# ═══════════════════════════════════════════════════════════════
# Open this file in VSCode.
# Set a breakpoint on the line `fee = calculate_late_fee(book_id, ...)`
# inside process_late_return().
# Run with the debugger (F5). When it pauses:
#   - Hover over `book_id` to see its value AND its type.
#   - That's the interface mismatch.

# ═══════════════════════════════════════════════════════════════
# TASK C — ANSWER THESE THREE QUESTIONS
# ═══════════════════════════════════════════════════════════════
# Write your answers in the comment slots below.
#
# 1. WHICH function is producing the wrong-typed value?
#    ANSWER:
#
# 2. WHICH function is making the assumption that fails?
#    ANSWER:
#
# 3. Both functions look correct on their own. So WHO owns the bug?
#    (Hint: contracts. Which docstring is being violated?)
#    ANSWER:

# ═══════════════════════════════════════════════════════════════
# TASK D — FIX IT (the right way)
# ═══════════════════════════════════════════════════════════════
# Fix the function whose docstring is being broken.
# DON'T loosen the receiving function — its contract is intentional.
# Run again. The fee should be 2.5.


if __name__ == '__main__':
    library = {"Hobbit": 1, "Dune": 2, "1984": 3}
    fee = process_late_return("Dune", days_late=5, catalog=library, fee_per_day=0.50)
    print(f"Late fee: ${fee}")
