# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_concert_singer():
    """Optimize concert-stadium pairings to maximize attendance"""

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

    # Data from the problem context
    concert_stadium_mapping = [(1, 101), (2, 102), (3, 103)]
    attendance_coefficients = {
        (1, 101): 120.0,
        (1, 102): 180.0,
        (2, 102): 250.0,
        (2, 103): 300.0,
        (3, 103): 400.0
    }
    stadium_capacity = {
        101: 5000,
        102: 10000,
        103: 15000
    }

    # Validate data lengths
    assert len(concert_stadium_mapping) > 0, "Concert-stadium mapping is empty"
    assert len(attendance_coefficients) > 0, "Attendance coefficients are empty"
    assert len(stadium_capacity) > 0, "Stadium capacity data is empty"

    # 2. VARIABLES
    x = model.addVars(concert_stadium_mapping, vtype=GRB.BINARY, name="x")

    # 3. OBJECTIVE FUNCTION
    model.setObjective(
        gp.quicksum(attendance_coefficients.get((i, j), 150) * x[i, j] for i, j in concert_stadium_mapping),
        GRB.MAXIMIZE
    )

    # 4. CONSTRAINTS

    # Stadium Capacity Constraints
    for j in stadium_capacity:
        model.addConstr(
            gp.quicksum(attendance_coefficients.get((i, j), 150) * x[i, j] for i, _ in concert_stadium_mapping if j == _)
            <= stadium_capacity[j],
            name=f"capacity_{j}"
        )

    # Concert Venue Assignment Constraints
    for i, _ in set(concert_stadium_mapping):
        model.addConstr(
            gp.quicksum(x[i, j] for _, j in concert_stadium_mapping if i == _) >= 1,
            name=f"venue_assignment_{i}"
        )

    # 5. SOLVING & RESULTS
    model.optimize()

    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i, j in concert_stadium_mapping:
            if x[i, j].x > 1e-6:
                print(f"Concert {i} at Stadium {j} is selected with expected attendance: {attendance_coefficients.get((i, j), 150)}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

    return model

# Run the optimization
optimize_concert_singer()