# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def machine_repair_optimization():
    # 1. MODEL & DATA SETUP
    model = gp.Model("machine_repair")
    
    # Data from the database
    repair_time_data = {
        (1, 101): 2.5,
        (2, 102): 3.0,
        (3, 103): 4.0
    }
    
    technician_capacity_data = {
        1: 5,
        2: 4,
        3: 3
    }
    
    machine_priority_data = {
        201: 1,
        202: 2,
        203: 3
    }
    
    # Extract unique technicians and repairs
    technicians = list(technician_capacity_data.keys())
    repairs = list(set(repair[1] for repair in repair_time_data.keys()))
    
    # Validate data consistency
    assert all(technician in technician_capacity_data for technician in technicians), "Missing technician capacity data"
    assert all((technician, repair) in repair_time_data for technician in technicians for repair in repairs), "Missing repair time data"
    
    # 2. VARIABLES
    x = model.addVars(technicians, repairs, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(gp.quicksum(repair_time_data[(i, j)] * x[i, j] for i in technicians for j in repairs), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    # Single Assignment per Repair
    for j in repairs:
        model.addConstr(gp.quicksum(x[i, j] for i in technicians) == 1, name=f"single_assignment_{j}")
    
    # Technician Capacity Limit
    for i in technicians:
        model.addConstr(gp.quicksum(x[i, j] for j in repairs) <= technician_capacity_data[i], name=f"capacity_{i}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in technicians:
            for j in repairs:
                if x[i, j].x > 1e-6:
                    print(f"Technician {i} assigned to repair {j}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
machine_repair_optimization()