# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_performance_attendance():
    """Optimize the allocation of theater members to performances."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="performance_attendance")
    
    # Data from the database
    member_ids = [1, 2, 3]
    performance_ids = [101, 102, 103]
    attendance_limits = [50, 60, 70]  # Corresponds to performance.Attendance
    member_availabilities = [5, 10, 15]  # Corresponds to member_availability.Availability
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(performance_ids) == len(attendance_limits), "Performance data length mismatch"
    assert len(member_ids) == len(member_availabilities), "Member data length mismatch"
    
    # Safe indexing
    safe_member_range = range(len(member_ids))
    safe_performance_range = range(len(performance_ids))
    
    # 2. VARIABLES
    # Decision variables: number of pieces performed by member i in performance j
    x = {(i, j): mdl.integer_var(name=f"x_{member_ids[i]}_{performance_ids[j]}", lb=0)
         for i in safe_member_range for j in safe_performance_range}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total number of pieces performed across all performances
    objective = mdl.sum(x[i, j] for i in safe_member_range for j in safe_performance_range)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Performance Attendance Constraints
    for j in safe_performance_range:
        mdl.add_constraint(mdl.sum(x[i, j] for i in safe_member_range) <= attendance_limits[j],
                           ctname=f"attendance_limit_{performance_ids[j]}")
    
    # Member Availability Constraints
    for i in safe_member_range:
        mdl.add_constraint(mdl.sum(x[i, j] for j in safe_performance_range) <= member_availabilities[i],
                           ctname=f"availability_limit_{member_ids[i]}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in safe_member_range:
            for j in safe_performance_range:
                value = solution.get_value(x[i, j])
                if value > 1e-6:
                    print(f"x[{member_ids[i]}, {performance_ids[j]}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
optimize_performance_attendance()