# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_party_host_assignment():
    """Optimize host assignments to parties using Gurobi."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("party_host")
    
    # Example data setup
    parties = [1, 2, 3]  # Party IDs
    hosts = [101, 102, 103, 104, 105]  # Host IDs
    max_hosts_per_party = 2  # Maximum number of hosts allowed per party
    
    # Validate array lengths before loops
    assert len(parties) > 0, "No parties available"
    assert len(hosts) > 0, "No hosts available"
    
    # 2. VARIABLES
    # Binary decision variables for host assignments
    x = {(i, j): model.addVar(vtype=GRB.BINARY, name=f"x_{i}_{j}") 
         for i in parties for j in hosts}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total number of host assignments
    model.setObjective(gp.quicksum(x[i, j] for i in parties for j in hosts), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    
    # Each party must have at least one host
    model.addConstrs((gp.quicksum(x[i, j] for j in hosts) >= 1 for i in parties), name="min_one_host")
    
    # No party should exceed the maximum number of hosts allowed
    model.addConstrs((gp.quicksum(x[i, j] for j in hosts) <= max_hosts_per_party for i in parties), name="max_hosts")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in parties:
            for j in hosts:
                if x[i, j].x > 1e-6:
                    print(f"Host {j} assigned to Party {i}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_party_host_assignment()