"""
tests/test_paren_reduction.py

Verifies that the printer emits minimal parentheses based on operator
precedence, rather than wrapping every binary expression unconditionally.
"""
import sys, os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from transpiler.transformer import transform
from transpiler.generator import generate
from transpiler.flags import CompilerFlags
import ast as pyast


def _gen(source: str) -> str:
    tree = pyast.parse(source)
    res = transform(tree)
    flags = CompilerFlags(no_runtime=True, fast=True)
    result = generate(res, flags=flags)
    return result.code


# ---------------------------------------------------------------------------
# Cases where parens should NOT appear
# ---------------------------------------------------------------------------

def test_simple_addition_no_parens():
    """a + b should not become (a + b)."""
    code = _gen("x[0] = a + b")
    assert "(a + b)" not in code, f"Unexpected parens: {code}"
    assert "a + b" in code, f"Expected 'a + b' in: {code}"
    print(f"[PASS] test_simple_addition_no_parens\n  → {code.strip()}")


def test_chained_addition_no_parens():
    """a + b + c is left-associative — no parens needed anywhere."""
    code = _gen("x[0] = a + b + c")
    # Should NOT have parens for the same-precedence level
    assert "((a + b) + c)" not in code
    print(f"[PASS] test_chained_addition_no_parens\n  → {code.strip()}")


def test_multiply_over_add_no_parens():
    """a + b * c — multiplication binds tighter, no parens needed on b*c."""
    code = _gen("x[0] = a + b * c")
    # b * c should NOT be wrapped since * > +
    assert "(b * c)" not in code, f"Unexpected parens on b*c: {code}"
    print(f"[PASS] test_multiply_over_add_no_parens\n  → {code.strip()}")


# ---------------------------------------------------------------------------
# Cases where parens ARE correct
# ---------------------------------------------------------------------------

def test_add_inside_multiply_keeps_parens():
    """(a + b) * c — addition must be wrapped because it binds looser than *."""
    code = _gen("x[0] = (a + b) * c")
    # The addition must be parenthesised because + < *
    assert "(a + b)" in code, f"Expected parens on (a+b): {code}"
    print(f"[PASS] test_add_inside_multiply_keeps_parens\n  → {code.strip()}")


def test_subtraction_right_associativity_keeps_parens():
    """a - (b - c) must keep parens because subtraction is left-associative."""
    code = _gen("x[0] = a - (b - c)")
    # right operand at same precedence → needs parens
    assert "(b - c)" in code, f"Expected parens on right side: {code}"
    print(f"[PASS] test_subtraction_right_associativity_keeps_parens\n  → {code.strip()}")


def test_complex_expression():
    """x = a * b + c / d — no outer parens, no unnecessary inner parens."""
    code = _gen("x[0] = a * b + c / d")
    # a*b and c/d should bind tighter — no parens needed for either sub-expr
    assert "(a * b + c / d)" not in code, f"Outermost parens found: {code}"
    print(f"[PASS] test_complex_expression\n  → {code.strip()}")


if __name__ == "__main__":
    test_simple_addition_no_parens()
    test_chained_addition_no_parens()
    test_multiply_over_add_no_parens()
    test_add_inside_multiply_keeps_parens()
    test_subtraction_right_associativity_keeps_parens()
    test_complex_expression()
    print("\n[ALL PASS] Parenthesis reduction tests.")
