# Complete DOCPLEX implementation - Retry Attempt 4

from docplex.mp.model import Model

def school_player_optimization():
    # 1. MODEL & DATA SETUP
    mdl = Model(name="school_player_optimization")
    
    # Data from the database schema
    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 = {
        'Forward': {'min_players': 2, 'max_players': 4},
        'Midfielder': {'min_players': 3, 'max_players': 5},
        'Defender': {'min_players': 3, 'max_players': 5}
    }
    
    school_data = {
        1: {'enrollment': 25, 'historical_performance': 0.9},
        2: {'enrollment': 20, 'historical_performance': 0.8},
        3: {'enrollment': 30, 'historical_performance': 0.7}
    }
    
    player_assignment = [
        {'player_id': 1, 'school_id': 1, 'position': 'Forward'},
        {'player_id': 2, 'school_id': 2, 'position': 'Midfielder'},
        {'player_id': 3, 'school_id': 3, 'position': 'Defender'},
        {'player_id': 4, 'school_id': 1, 'position': 'Midfielder'},
        {'player_id': 5, 'school_id': 2, 'position': 'Forward'},
        {'player_id': 6, 'school_id': 3, 'position': 'Midfielder'},
        {'player_id': 7, 'school_id': 1, 'position': 'Defender'},
        {'player_id': 8, 'school_id': 2, 'position': 'Midfielder'},
        {'player_id': 9, 'school_id': 3, 'position': 'Forward'},
        {'player_id': 10, 'school_id': 1, 'position': 'Forward'}
    ]
    
    # 2. VARIABLES
    # Binary decision variable: x[p, s] = 1 if player p is assigned to school s
    x = {(p['player_id'], s): mdl.binary_var(name=f"x_{p['player_id']}_{s}") 
         for p in player_assignment for s in school_data.keys()}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total performance weight across all player assignments
    objective = mdl.sum(performance_weight[s, p['position']] * x[p['player_id'], s] 
                        for p in player_assignment for s in school_data.keys())
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # School Enrollment Limits
    for s in school_data.keys():
        mdl.add_constraint(mdl.sum(x[p['player_id'], s] for p in player_assignment) <= school_data[s]['enrollment'], 
                           ctname=f"enrollment_limit_{s}")
    
    # Position Constraints
    for s in school_data.keys():
        for pos, constraints in position_constraints.items():
            players_in_position = [p['player_id'] for p in player_assignment if p['position'] == pos]
            mdl.add_constraint(mdl.sum(x[p, s] for p in players_in_position) >= constraints['min_players'], 
                               ctname=f"min_players_{s}_{pos}")
            mdl.add_constraint(mdl.sum(x[p, s] for p in players_in_position) <= constraints['max_players'], 
                               ctname=f"max_players_{s}_{pos}")
    
    # Minimum Team Size
    for s in school_data.keys():
        mdl.add_constraint(mdl.sum(x[p['player_id'], s] for p in player_assignment) >= 11, 
                           ctname=f"min_team_size_{s}")
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for p in player_assignment:
            for s in school_data.keys():
                if solution.get_value(x[p['player_id'], s]) > 0.5:
                    print(f"Player {p['player_id']} assigned to School {s}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Execute the optimization
school_player_optimization()