#!/usr/bin/env python3
"""
DOCplex implementation for university extracurricular activity allocation problem.
"""

from docplex.mp.model import Model

def optimize_activity_allocation():
    """Optimize the allocation of students and faculty to extracurricular activities."""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="activity_allocation")
    
    # Data from the problem description
    students = [101, 102, 103]
    faculty = [201, 202, 203]
    activities = [1, 2, 3]
    
    # Activity capacities
    activity_capacities = {
        1: 10,
        2: 15,
        3: 20
    }
    
    # CRITICAL: Validate array lengths to prevent IndexError
    assert len(students) > 0, "No students provided"
    assert len(faculty) > 0, "No faculty provided"
    assert len(activities) > 0, "No activities provided"
    assert len(activity_capacities) == len(activities), "Activity capacity mismatch"
    
    # 2. VARIABLES
    # Decision variables
    x = {(s, a): mdl.binary_var(name=f"x_{s}_{a}") for s in students for a in activities}
    y = {(f, a): mdl.binary_var(name=f"y_{f}_{a}") for f in faculty for a in activities}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total participation
    total_participation = mdl.sum(x[s, a] for s in students for a in activities) + \
                          mdl.sum(y[f, a] for f in faculty for a in activities)
    mdl.maximize(total_participation)
    
    # 4. CONSTRAINTS
    
    # Student Participation Limit: Each student can participate in at most one activity
    for s in students:
        mdl.add_constraint(mdl.sum(x[s, a] for a in activities) <= 1, ctname=f"student_limit_{s}")
    
    # Faculty Participation Limit: Each faculty member can participate in at most two activities
    for f in faculty:
        mdl.add_constraint(mdl.sum(y[f, a] for a in activities) <= 2, ctname=f"faculty_limit_{f}")
    
    # Activity Capacity Limit: Total participants in each activity must not exceed its capacity
    for a in activities:
        total_participants = mdl.sum(x[s, a] for s in students) + mdl.sum(y[f, a] for f in faculty)
        mdl.add_constraint(total_participants <= activity_capacities[a], ctname=f"capacity_limit_{a}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for s in students:
            for a in activities:
                if solution.get_value(x[s, a]) > 0:
                    print(f"Student {s} participates in activity {a}")
        for f in faculty:
            for a in activities:
                if solution.get_value(y[f, a]) > 0:
                    print(f"Faculty {f} participates in activity {a}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
if __name__ == "__main__":
    optimize_activity_allocation()