#!/usr/bin/env python3
"""
Gurobipy Implementation for University Activity Allocation Problem
"""

import gurobipy as gp
from gurobipy import GRB

def optimize_activity_allocation():
    """Optimize the allocation of students and faculty to activities."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("activity_allocation")
    
    # Example data (replace with actual data from database)
    students = [101, 102, 103]
    faculty = [201, 202, 203]
    activities = [1, 2, 3]
    
    # Activity capacities
    activity_capacity = {
        1: 10,
        2: 15,
        3: 20
    }
    
    # Validate array lengths before loops
    assert len(students) > 0, "No students provided"
    assert len(faculty) > 0, "No faculty provided"
    assert len(activities) > 0, "No activities provided"
    assert len(activity_capacity) == len(activities), "Activity capacity mismatch"
    
    # 2. VARIABLES
    # Decision variables for student participation
    x = {(s, a): model.addVar(vtype=GRB.BINARY, name=f"x_{s}_{a}") 
         for s in students for a in activities}
    
    # Decision variables for faculty participation
    y = {(f, a): model.addVar(vtype=GRB.BINARY, name=f"y_{f}_{a}") 
         for f in faculty for a in activities}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total participation
    model.setObjective(
        gp.quicksum(x[s, a] for s in students for a in activities) +
        gp.quicksum(y[f, a] for f in faculty for a in activities),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # Student Participation Limit: Each student can participate in at most one activity
    for s in students:
        model.addConstr(
            gp.quicksum(x[s, a] for a in activities) <= 1,
            name=f"student_limit_{s}"
        )
    
    # Faculty Participation Limit: Each faculty member can participate in at most two activities
    for f in faculty:
        model.addConstr(
            gp.quicksum(y[f, a] for a in activities) <= 2,
            name=f"faculty_limit_{f}"
        )
    
    # Activity Capacity Limit: Total participants in each activity must not exceed its capacity
    for a in activities:
        model.addConstr(
            gp.quicksum(x[s, a] for s in students) +
            gp.quicksum(y[f, a] for f in faculty) <= activity_capacity[a],
            name=f"activity_capacity_{a}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for s in students:
            for a in activities:
                if x[s, a].x > 0.5:
                    print(f"Student {s} participates in activity {a}")
        for f in faculty:
            for a in activities:
                if y[f, a].x > 0.5:
                    print(f"Faculty {f} participates in activity {a}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

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