"""
tests/test_cfg_validator.py — Adversarial tests for the CFG validator.
"""

import unittest
from transpiler.middleend import ir, cfg, cfg_builder
from transpiler.optimization import cfg_validator

class TestCFGValidator(unittest.TestCase):
    def test_valid_function(self):
        # Build a simple valid function
        # def test(x):
        #   if x: return 1
        #   else: return 0
        
        module = ir.IRModule(body=[
            ir.IRFunctionDef(
                name="test_func",
                args=["x"],
                body=[
                    ir.IRIfStatement(
                        condition=ir.IRVariable(name="x"),
                        then_block=[ir.IRReturnStatement(value=ir.IRLiteral(value=1))],
                        else_block=[ir.IRReturnStatement(value=ir.IRLiteral(value=0))]
                    )
                ]
            )
        ])

        print("\nIR TREE DUMP for test_valid_function:")
        for stmt in module.body:
            print(f"  {type(stmt).__name__}: {stmt.__dict__}")
            if isinstance(stmt, ir.IRFunctionDef):
                for s in stmt.body:
                    print(f"    {type(s).__name__}: {s.__dict__}")
        
        # Should NOT raise
        try:
            cfg_validator.validate(module)
        except Exception as e:
            # Debug: print the CFG produced
            func = module.body[0]
            func_cfg = cfg_builder.build_cfg(func)
            print(f"\nCFG DUMP for test_valid_function (Entry ID: {func_cfg.entry_block.id}):")
            for bid, block in func_cfg.blocks.items():
                print(f"  Block {bid}:")
                for st in block.statements:
                    print(f"    {st}")
                print(f"    Successors: {[b.id for b in block.successors]}")
                print(f"    Predecessors: {[b.id for b in block.predecessors]}")
            raise e

    def test_missing_terminator(self):
        # Manually construct a block with no terminator
        block = cfg.BasicBlock(id=0)
        block.statements = [ir.IRAssignment(target=ir.IRVariable(name="a"), value=ir.IRLiteral(value=1))]
        
        target_cfg = cfg.CFG(entry_block=block, blocks={0: block})
        
        validator = cfg_validator.CFGValidator()
        with self.assertRaisesRegex(cfg_validator.CFGValidationError, "no valid terminator"):
            validator.validate_cfg(target_cfg)

    def test_edge_asymmetry(self):
        b0 = cfg.BasicBlock(id=0)
        b1 = cfg.BasicBlock(id=1)
        
        b0.statements = [ir.IRJump(target_block_id=1)]
        b1.statements = [ir.IRReturnStatement(value=ir.IRLiteral(value=None))]
        
        # B0 -> B1, but B1 doesn't know about B0 as predecessor
        b0.successors = [b1]
        b1.predecessors = [] # Error here
        
        target_cfg = cfg.CFG(entry_block=b0, blocks={0: b0, 1: b1})
        
        validator = cfg_validator.CFGValidator()
        with self.assertRaisesRegex(cfg_validator.CFGValidationError, "Edge asymmetry"):
            validator.validate_cfg(target_cfg)

    def test_phi_mismatch(self):
        b0 = cfg.BasicBlock(id=0)
        b1 = cfg.BasicBlock(id=1)
        b2 = cfg.BasicBlock(id=2)
        
        b0.statements = [ir.IRBranch(condition=ir.IRLiteral(value=True), true_block_id=1, false_block_id=2)]
        b1.statements = [ir.IRJump(target_block_id=3)]
        b2.statements = [ir.IRJump(target_block_id=3)]
        
        b3 = cfg.BasicBlock(id=3)
        # Phi in B3 expecting inputs from B1 and B2
        phi = ir.IRPhi(options={1: ir.IRLiteral(value=1), 2: ir.IRLiteral(value=2)})
        phi_assign = ir.IRAssignment(target=ir.IRVariable(name="x"), value=phi)
        b3.statements = [phi_assign, ir.IRReturnStatement(value=ir.IRLiteral(value=None))]
        
        # Link them correctly first
        b0.successors = [b1, b2]
        b1.predecessors = [b0]; b1.successors = [b3]
        b2.predecessors = [b0]; b2.successors = [b3]
        b3.predecessors = [b1, b2]
        
        target_cfg = cfg.CFG(entry_block=b0, blocks={0: b0, 1: b1, 2: b2, 3: b3})
        
        validator = cfg_validator.CFGValidator()
        # Should pass initially
        validator.validate_cfg(target_cfg)
        
        # Now fail it: remove one input from Phi
        phi.options = {1: ir.IRLiteral(value=1)} # Missing index 2
        with self.assertRaisesRegex(cfg_validator.CFGValidationError, "Phi inputs .* do not match block predecessors"):
            validator.validate_cfg(target_cfg)

if __name__ == "__main__":
    unittest.main()
