# Exam 0 Tests - Problem 3 (5 of 20 points)
import math
import iotest

# Test cases: ( (input_fahrenheit,), (expected_temperature, expected_string) )
test_cases = [
    ((68,), (20.0, "is nice")), # 20 C
    ((41,), (5.0, "put a coat on")), # 5 C
    ((212,), (100.0, "would be a world record. Are you sure?")), # 100 C (above record high)

    # World record checks
    ((140,), (60.0, "would be a world record. Are you sure?")), # Above high record
    ((-130,), (-90.0, "would be a world record. Are you sure?")), # Below low record
    ((134.1,), (56.72222222222222, "is hot")), # Exact high record (not >)
    ((-128.6,), (-89.22222222222223, "is ice")), # Exact low record (not <)

    # Temperature category checks
    ((86,), (30.0, "is hot")),      # 30 C (>= 25)
    ((77,), (25.0, "is hot")),      # 25 C (>= 25)
    ((76,), (24.444444444444443, "is nice")), # ~24.4 C (>= 15)
    ((59,), (15.0, "is nice")),     # 15 C (>= 15)
    ((58,), (14.444444444444445, "put a coat on")), # ~14.4 C (> 0)
    ((33,), (0.5555555555555556, "put a coat on")), # ~0.6 C (> 0)
    ((32,), (0.0, "is ice")),       # 0 C (<= 0)
    ((-4,), (-20.0, "is ice")),     # -20 C (<= 0)
]

def make_string_exact_testcase(args, expected_str):
    # Check that output string matches exactly after the first space
    # We'll use a custom comparator that splits after the first space and compares
    def string_comparator(expected_output, actual_output):
        # Split after first space
        parts = actual_output.split(" ", maxsplit=1)
        actual_str = parts[1] if len(parts) > 1 else ""
        passed = actual_str == expected_output
        msg = ""
        if not passed:
            msg = f"    Expected string: {expected_output}\n    Observed string: {actual_str}"
        return passed, msg
    return {
        "args": [str(a) for a in args],
        "expected_output": expected_str,
        "comparator": string_comparator
    }

def make_temp_floatclose_testcase(args, expected_temp):
    # Custom comparator: checks first float in output is close to expected_temp
    def floatclose_comparator(expected_output, actual_output):
        try:
            first_token = actual_output.split(" ", maxsplit=1)[0]
            actual_float = float(first_token)
            passed = math.isclose(actual_float, float(expected_output), rel_tol=0.01)
        except Exception as e:
            passed = False
            actual_float = None
        msg = ""
        if not passed:
            msg = (
                f"    Expected temperature (float): {expected_output}\n"
                f"    Observed temperature (float): {actual_float}\n"
                f"    Full output: {actual_output}"
            )
        return passed, msg
    return {
        "args": [str(a) for a in args],
        "expected_output": str(expected_temp),
        "comparator": floatclose_comparator
    }

# Build test cases for iotest
string_exact_cases = [
    make_string_exact_testcase(args, expected_str)
    for args, (expected_temp, expected_str) in test_cases
]

temp_floatclose_cases = [
    make_temp_floatclose_testcase(args, expected_temp)
    for args, (expected_temp, expected_str) in test_cases
]

if __name__ == "__main__":
    iotest.run_assignment_tests("p3.py", temp_floatclose_cases + string_exact_cases)
