# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_roller_coaster_maintenance():
    """Optimize maintenance hours allocation for roller coasters to minimize downtime."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("roller_coaster_maintenance")
    
    # Data from the database
    downtime_coefficients = [0.4, 0.35, 0.25]
    minimum_hours = [3.0, 4.0, 2.5]
    maximum_hours = [10.0, 12.0, 9.0]
    total_available_hours = 22
    
    # Validate array lengths
    n_coasters = len(downtime_coefficients)
    assert len(minimum_hours) == len(maximum_hours) == n_coasters, "Array length mismatch"
    
    # 2. VARIABLES
    # Create a dictionary of variables for maintenance hours
    maintenance_hours = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_{i+1}", lb=0) 
                         for i in range(n_coasters)}
    
    # 3. OBJECTIVE FUNCTION
    # Minimize total downtime
    model.setObjective(gp.quicksum(downtime_coefficients[i] * maintenance_hours[i] for i in range(n_coasters)), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    # Total maintenance hours constraint
    model.addConstr(gp.quicksum(maintenance_hours[i] for i in range(n_coasters)) <= total_available_hours, name="total_hours")
    
    # Minimum and maximum maintenance hours constraints
    for i in range(n_coasters):
        model.addConstr(maintenance_hours[i] >= minimum_hours[i], name=f"min_hours_{i+1}")
        model.addConstr(maintenance_hours[i] <= maximum_hours[i], name=f"max_hours_{i+1}")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_coasters):
            print(f"x[{i+1}] = {maintenance_hours[i].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_roller_coaster_maintenance()