# Exam 2 Tests - Problem 2 (10 of 20 points)
import pytest
import subprocess

basename = "p2"

import p2

# Test cases as tuples: ((loan_amount, interest_rate, payment), expected_months)
test_cases = [
    # Standard cases
    ((10.0, 0.1, 3.0), 5),              # Example from the problem statement
    ((1000.0, 0.05, 100.0), 15),        # Common loan scenario
    ((5000.0, 0.01, 500.0), 11),        # Low interest rate
    ((2000.0, 0.2, 600.0), 7),          # High interest rate, high payment
    
    # Edge cases
    ((0.0, 0.1, 100.0), 0),             # Zero loan amount
    ((100.0, 0.0, 10.0), 10),           # Zero interest rate
    ((100.0, 0.1, 100.0), 2),           # Payment equals loan amount (still need to pay interest)
    ((100.0, 0.1, 110.0), 1),           # Payment covers loan plus first interest
    ((100.0, 0.1, 1000.0), 1),          # Payment much larger than needed
    
    # Cases where loan would never be paid
    ((100.0, 0.2, 10.0), -1),           # Interest exceeds payment (20 > 10)
    ((1000.0, 0.05, 50.0), -1),         # Interest equals payment (50 = 50)
    ((500.0, 0.1, 49.0), -1),           # Interest slightly less than payment, but not enough to make progress
    
    # Larger and complex cases
    ((10000.0, 0.03, 400.0), 47),       # Larger loan with moderate interest
    ((50000.0, 0.006, 1200.0), 49),     # Mortgage-like scenario
    ((7500.0, 0.02, 1000.0), 9)         # Higher payment relative to loan
]


# test the function itself
@pytest.mark.parametrize("inputs, expected", test_cases)
@pytest.mark.timeout(3)
def test_payoff_months(inputs, expected):
    (loan_amount, interest_rate, payment) = inputs
    assert expected == p2.payoff_months(loan_amount, interest_rate, payment)


def run_with_args(filename, *args):
    try:
        args = [str(a) for a in args]
        command = ["python3", filename + ".py", *args]
        return subprocess.check_output(command, text=True).rstrip("\n")
    except subprocess.CalledProcessError as e:
        print(f"\nProgram crashed with exit code {e.returncode}")
        if e.output:
            print(f"Output before crash: {e.output}")
        if e.stderr:
            print(f"Error message: {e.stderr}")
        raise AssertionError() from None

# test the main program behavior, which should match the function's
@pytest.mark.parametrize("inputs, expected", test_cases[:5])
@pytest.mark.timeout(3)
def test_main_program(inputs, expected):
    assert run_with_args(basename, *inputs) == str(expected)

if __name__ == "__main__":
    pytest.main(["p2_test.py", "-vv",  "-p", "no:faulthandler"])
