# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_riding_club():
    """Optimize player-coach assignments for the riding club"""

    # 1. MODEL & DATA SETUP
    model = gp.Model("riding_club")

    # Data from the database
    compatibility_scores = [
        (1, 1, 0.85),
        (1, 2, 0.65),
        (2, 1, 0.75),
        (2, 3, 0.6),
        (3, 2, 0.9),
        (3, 3, 0.7)
    ]
    
    coach_constraints = {
        1: 5,
        2: 4,
        3: 6
    }
    
    min_compatibility_score = 0.7

    # Extract unique players and coaches
    players = set(player for player, _, _ in compatibility_scores)
    coaches = set(coach for _, coach, _ in compatibility_scores)

    # CRITICAL: Validate data consistency
    assert all(isinstance(score, float) for _, _, score in compatibility_scores), "Invalid score type"

    # 2. VARIABLES
    # Decision variables: x[i, j] = 1 if player i is assigned to coach j
    x = model.addVars(players, coaches, vtype=GRB.BINARY, name="x")

    # 3. OBJECTIVE FUNCTION
    # Maximize total compatibility score
    model.setObjective(
        gp.quicksum(score * x[i, j] for i, j, score in compatibility_scores),
        GRB.MAXIMIZE
    )

    # 4. CONSTRAINTS

    # Coach capacity constraints
    model.addConstrs(
        (gp.quicksum(x[i, j] for i in players) <= coach_constraints[j] for j in coaches),
        name="coach_capacity"
    )

    # Player assignment constraints
    model.addConstrs(
        (gp.quicksum(x[i, j] for j in coaches) <= 1 for i in players),
        name="player_assignment"
    )

    # Compatibility threshold constraints
    model.addConstrs(
        (x[i, j] == 0 for i, j, score in compatibility_scores if score < min_compatibility_score),
        name="compatibility_threshold"
    )

    # 5. SOLVING & RESULTS
    model.optimize()

    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i, j in x.keys():
            if x[i, j].x > 1e-6:
                print(f"Player {i} assigned to Coach {j}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

    return model

# Run the optimization
optimize_riding_club()