# Complete DOCPLEX implementation

from docplex.mp.model import Model

def optimize_faculty_allocation():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="faculty_allocation")
    
    # Example data setup
    participation_scores = [
        (1, 101, 12.0),
        (2, 102, 18.5),
        (3, 103, 14.0),
        (1, 102, 10.0),
        (2, 103, 16.0)
    ]
    
    max_activities = {1: 2, 2: 2, 3: 1}  # Example max activities per faculty
    min_faculty = {101: 1, 102: 1, 103: 1}  # Example min faculty per activity
    
    # Extract unique faculty and activity IDs
    faculty_ids = set(fac_id for fac_id, _, _ in participation_scores)
    activity_ids = set(act_id for _, act_id, _ in participation_scores)
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(participation_scores) > 0, "Participation scores data is empty"
    
    # 2. VARIABLES
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i in faculty_ids for j in activity_ids}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(score * x[i, j] for i, j, score in participation_scores)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Faculty Availability Constraint
    for i in faculty_ids:
        mdl.add_constraint(mdl.sum(x[i, j] for j in activity_ids) <= max_activities[i], ctname=f"max_activities_{i}")
    
    # Activity Staffing Constraint
    for j in activity_ids:
        mdl.add_constraint(mdl.sum(x[i, j] for i in faculty_ids) >= min_faculty[j], ctname=f"min_faculty_{j}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i in faculty_ids:
            for j in activity_ids:
                value = solution.get_value(x[i, j])
                if value > 1e-6:
                    print(f"x[{i},{j}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")

    return mdl

# Run the optimization
optimize_faculty_allocation()