# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_student_engagement():
    """Optimize student engagement by allocating hours to games and sports."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("student_engagement")
    
    # Sample data based on the provided problem context
    students = [1, 2, 3]
    games = [101, 102, 103]
    sports = ['Basketball', 'Soccer', 'Tennis']
    
    # Engagement scores from the database
    game_engagement_scores = {
        (1, 101): 1.8,
        (2, 102): 2.2,
        (3, 103): 1.5
    }
    
    sport_engagement_scores = {
        (1, 'Basketball'): 1.5,
        (2, 'Soccer'): 1.8,
        (3, 'Tennis'): 2.0
    }
    
    # 2. VARIABLES
    # Decision variables for hours allocated to games and sports
    x = model.addVars(students, games, vtype=GRB.CONTINUOUS, name="x", lb=0)
    y = model.addVars(students, sports, vtype=GRB.CONTINUOUS, name="y", lb=0)
    
    # 3. OBJECTIVE FUNCTION
    # Maximize total engagement score
    model.setObjective(
        gp.quicksum(game_engagement_scores[StuID, GameID] * x[StuID, GameID] for StuID in students for GameID in games) +
        gp.quicksum(sport_engagement_scores[StuID, SportName] * y[StuID, SportName] for StuID in students for SportName in sports),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    # Total hours constraint
    model.addConstrs(
        (gp.quicksum(x[StuID, GameID] for GameID in games) + gp.quicksum(y[StuID, SportName] for SportName in sports) <= 20
         for StuID in students),
        name="total_hours"
    )
    
    # Scholarship sports hours constraint (assuming all students are on scholarship for simplicity)
    model.addConstrs(
        (gp.quicksum(y[StuID, SportName] for SportName in sports) >= 5 for StuID in students),
        name="scholarship_sports_hours"
    )
    
    # Maximum gaming hours constraint
    model.addConstrs(
        (gp.quicksum(x[StuID, GameID] for GameID in games) <= 15 for StuID in students),
        name="max_gaming_hours"
    )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for StuID in students:
            for GameID in games:
                if x[StuID, GameID].x > 1e-6:
                    print(f"x[{StuID}, {GameID}] = {x[StuID, GameID].x:.3f}")
            for SportName in sports:
                if y[StuID, SportName].x > 1e-6:
                    print(f"y[{StuID}, {SportName}] = {y[StuID, SportName].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
optimize_student_engagement()