# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_flight_schedule():
    """Optimize flight scheduling to minimize operational costs."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("flight_optimization")
    
    # Data from the problem context
    flights = [
        {"flno": 101, "price": 5000.0, "distance": 300.0},
        {"flno": 102, "price": 7500.0, "distance": 450.0},
        {"flno": 103, "price": 10000.0, "distance": 600.0}
    ]
    
    aircrafts = [
        {"aid": 1, "distance": 500.0},
        {"aid": 2, "distance": 700.0},
        {"aid": 3, "distance": 1000.0}
    ]
    
    employees = [
        {"eid": 10, "salary": 3000.0},
        {"eid": 20, "salary": 4000.0},
        {"eid": 30, "salary": 5000.0}
    ]
    
    budget = 12000.0  # Predefined budget for employee salaries
    
    # Validate data lengths
    n_flights = len(flights)
    n_aircrafts = len(aircrafts)
    n_employees = len(employees)
    
    assert n_flights > 0 and n_aircrafts > 0 and n_employees > 0, "Data arrays must not be empty"
    
    # 2. VARIABLES
    x = model.addVars(n_flights, n_aircrafts, n_employees, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(
            (flights[f]["price"] + employees[e]["salary"]) * x[f, a, e]
            for f in range(n_flights)
            for a in range(n_aircrafts)
            for e in range(n_employees)
        ),
        GRB.MINIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Flight Assignment Constraint
    model.addConstrs(
        (gp.quicksum(x[f, a, e] for a in range(n_aircrafts) for e in range(n_employees)) == 1
         for f in range(n_flights)),
        name="flight_assignment"
    )
    
    # Aircraft Distance Capability Constraint
    model.addConstrs(
        (gp.quicksum(flights[f]["distance"] * x[f, a, e] for f in range(n_flights)) <= aircrafts[a]["distance"]
         for a in range(n_aircrafts) for e in range(n_employees)),
        name="aircraft_distance"
    )
    
    # Employee Salary Budget Constraint
    model.addConstr(
        gp.quicksum(employees[e]["salary"] * x[f, a, e] for f in range(n_flights) for a in range(n_aircrafts) for e in range(n_employees)) <= budget,
        name="salary_budget"
    )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for f in range(n_flights):
            for a in range(n_aircrafts):
                for e in range(n_employees):
                    if x[f, a, e].x > 1e-6:
                        print(f"Flight {flights[f]['flno']} assigned to Aircraft {aircrafts[a]['aid']} and Employee {employees[e]['eid']}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_flight_schedule()