# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def optimize_debate_assignments():
    # 1. MODEL & DATA SETUP
    model = gp.Model("DebateAssignmentOptimization")
    
    # Data from the provided SQL schema and realistic data
    participants = [101, 102, 103]
    debates = [1, 2, 3]
    sides = ['Affirmative', 'Negative']
    
    audience_sizes = {1: 150, 2: 200, 3: 100}
    ages = {101: 25, 102: 30, 103: 35}
    
    # CRITICAL: Validate array lengths before loops
    assert len(participants) == len(ages), "Participants and ages length mismatch"
    assert len(debates) == len(audience_sizes), "Debates and audience sizes length mismatch"
    
    # 2. VARIABLES
    x = model.addVars(participants, debates, sides, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(audience_sizes[j] * x[i, j, k] for i in participants for j in debates for k in sides),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Participant Limit: Each participant can be assigned to no more than one debate
    for i in participants:
        model.addConstr(
            gp.quicksum(x[i, j, k] for j in debates for k in sides) <= 1,
            name=f"participant_limit_{i}"
        )
    
    # Side Balance: For each debate, the number of participants assigned to the Affirmative side must equal the number assigned to the Negative side
    for j in debates:
        model.addConstr(
            gp.quicksum(x[i, j, 'Affirmative'] for i in participants) ==
            gp.quicksum(x[i, j, 'Negative'] for i in participants),
            name=f"side_balance_{j}"
        )
    
    # Minimum Participation: Each debate must have at least one participant assigned to it
    for j in debates:
        model.addConstr(
            gp.quicksum(x[i, j, k] for i in participants for k in sides) >= 1,
            name=f"min_participation_{j}"
        )
    
    # Age-Based Constraint: The number of debates a participant can join is limited by their age, specifically by dividing their age by 25
    for i in participants:
        model.addConstr(
            gp.quicksum(x[i, j, k] for j in debates for k in sides) <= ages[i] // 25,
            name=f"age_constraint_{i}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in participants:
            for j in debates:
                for k in sides:
                    if x[i, j, k].x > 1e-6:
                        print(f"Participant {i} assigned to Debate {j} on {k} side")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
optimize_debate_assignments()