# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def optimize_player_assignment():
    # 1. MODEL & DATA SETUP
    model = gp.Model("school_player_assignment")
    
    # Data from the provided schema
    schools = [1, 2, 3]
    positions = ['Forward', 'Midfielder', 'Defender']
    players = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    
    # Performance weights
    performance_weight = {
        (1, 'Forward'): 0.9,
        (1, 'Midfielder'): 0.8,
        (1, 'Defender'): 0.7,
        (2, 'Forward'): 0.8,
        (2, 'Midfielder'): 0.9,
        (2, 'Defender'): 0.8,
        (3, 'Forward'): 0.7,
        (3, 'Midfielder'): 0.8,
        (3, 'Defender'): 0.9
    }
    
    # Position constraints
    position_constraints = {
        'Forward': {'min_players': 2, 'max_players': 4},
        'Midfielder': {'min_players': 3, 'max_players': 5},
        'Defender': {'min_players': 3, 'max_players': 5}
    }
    
    # School enrollment and historical performance
    school_data = {
        1: {'enrollment': 25, 'historical_performance': 0.9},
        2: {'enrollment': 20, 'historical_performance': 0.8},
        3: {'enrollment': 30, 'historical_performance': 0.7}
    }
    
    # Player positions (assuming each player has a fixed position)
    player_positions = {
        1: 'Forward',
        2: 'Midfielder',
        3: 'Defender',
        4: 'Forward',
        5: 'Midfielder',
        6: 'Defender',
        7: 'Forward',
        8: 'Midfielder',
        9: 'Defender',
        10: 'Forward'
    }
    
    # 2. VARIABLES
    x = model.addVars(players, schools, vtype=GRB.BINARY, name="x")
    
    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(performance_weight[(s, player_positions[p])] * x[p, s] 
                    for p in players for s in schools),
        GRB.MAXIMIZE
    )
    
    # 4. CONSTRAINTS
    
    # School Enrollment Limits
    for s in schools:
        model.addConstr(
            gp.quicksum(x[p, s] for p in players) <= school_data[s]['enrollment'],
            name=f"enrollment_limit_{s}"
        )
    
    # Position Constraints
    for s in schools:
        for pos in positions:
            players_in_pos = [p for p in players if player_positions[p] == pos]
            model.addConstr(
                gp.quicksum(x[p, s] for p in players_in_pos) >= position_constraints[pos]['min_players'],
                name=f"min_players_{s}_{pos}"
            )
            model.addConstr(
                gp.quicksum(x[p, s] for p in players_in_pos) <= position_constraints[pos]['max_players'],
                name=f"max_players_{s}_{pos}"
            )
    
    # Minimum Team Size
    for s in schools:
        model.addConstr(
            gp.quicksum(x[p, s] for p in players) >= 11,
            name=f"min_team_size_{s}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for p in players:
            for s in schools:
                if x[p, s].x > 0.5:
                    print(f"Player {p} assigned to School {s}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Execute the optimization
optimize_player_assignment()