# Complete DOCPLEX implementation

from docplex.mp.model import Model

def concert_singer_optimization():
    """Concert Singer Optimization using DOCPLEX"""
    
    # 1. MODEL & DATA SETUP
    mdl = Model(name="concert_singer")
    
    # Data from the problem statement
    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
    }
    
    # Extract unique concerts and stadiums
    concerts = set(concert_id for concert_id, _ in concert_stadium_mapping)
    stadiums = set(stadium_id for _, stadium_id in concert_stadium_mapping)
    
    # CRITICAL: Validate data consistency
    assert all((concert_id, stadium_id) in attendance_coefficients for concert_id, stadium_id in concert_stadium_mapping), "Missing coefficients"
    assert all(stadium_id in stadium_capacity for _, stadium_id in concert_stadium_mapping), "Missing capacities"
    
    # 2. VARIABLES
    x = {(i, j): mdl.binary_var(name=f"x_{i}_{j}") for i, j in concert_stadium_mapping}
    
    # 3. OBJECTIVE FUNCTION
    objective = mdl.sum(attendance_coefficients[i, j] * x[i, j] for i, j in concert_stadium_mapping)
    mdl.maximize(objective)
    
    # 4. CONSTRAINTS
    
    # Stadium Capacity Constraints
    for j in stadiums:
        mdl.add_constraint(
            mdl.sum(attendance_coefficients[i, j] * x[i, j] for i in concerts if (i, j) in concert_stadium_mapping) <= stadium_capacity[j],
            ctname=f"capacity_{j}"
        )
    
    # Concert Venue Assignment Constraints
    for i in concerts:
        mdl.add_constraint(
            mdl.sum(x[i, j] for j in stadiums if (i, j) in concert_stadium_mapping) >= 1,
            ctname=f"concert_{i}_venue"
        )
    
    # 5. SOLVING & RESULTS
    solution = mdl.solve()
    
    if solution:
        print(f"Optimal value: {solution.objective_value}")
        for i, j in concert_stadium_mapping:
            value = solution.get_value(x[i, j])
            if value > 1e-6:
                print(f"x[{i},{j}] = {value:.3f}")
    else:
        print("No solution found")
        print(f"Status: {mdl.solve_details.status}")
    
    return mdl

# Run the optimization
concert_singer_optimization()