# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def party_host_optimization():
    # 1. MODEL & DATA SETUP
    model = gp.Model("party_host_optimization")
    
    # Data from the database
    cost_per_host = {1: 50, 2: 70, 3: 60}
    min_hosts = {1: 2, 2: 3, 3: 4}
    max_hosts = {1: 4, 2: 5, 3: 6}
    expertise_match = {(1, 1): True, (1, 2): False, (2, 2): True}
    
    # Extract sets
    parties = set(min_hosts.keys())
    hosts = set(cost_per_host.keys())
    
    # Validate array lengths
    assert len(cost_per_host) > 0, "No hosts found"
    assert len(min_hosts) > 0, "No parties found"
    assert len(max_hosts) > 0, "No max hosts data found"
    assert len(expertise_match) > 0, "No expertise match data found"
    
    # 2. VARIABLES
    assign = model.addVars(parties, hosts, vtype=GRB.BINARY, name="assign")
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(gp.quicksum(cost_per_host[h] * assign[p, h] for p in parties for h in hosts), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    
    # Minimum hosts per party
    for p in parties:
        model.addConstr(gp.quicksum(assign[p, h] for h in hosts) >= min_hosts[p], name=f"min_hosts_{p}")
    
    # Maximum hosts per party
    for p in parties:
        model.addConstr(gp.quicksum(assign[p, h] for h in hosts) <= max_hosts[p], name=f"max_hosts_{p}")
    
    # Expertise matching
    for p in parties:
        for h in hosts:
            if (p, h) in expertise_match:
                model.addConstr(assign[p, h] <= expertise_match[p, h], name=f"expertise_{p}_{h}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for p in parties:
            for h in hosts:
                if assign[p, h].x > 0.5:
                    print(f"Host {h} assigned to Party {p}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
party_host_optimization()