# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def film_rank_optimization():
    """Optimize film distribution to maximize total gross revenue."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("film_rank")
    
    # Data from the database
    film_market_data = [
        (1, 101, 12000.0),
        (1, 102, 15000.0),
        (2, 101, 8000.0),
        (2, 103, 20000.0),
        (3, 102, 25000.0)
    ]
    
    # Budget limit
    B = 50000.0
    
    # Validate data length
    assert all(len(record) == 3 for record in film_market_data), "Data length mismatch"
    
    # 2. VARIABLES
    # Create a dictionary of binary decision variables
    x = {(film_id, market_id): model.addVar(vtype=GRB.BINARY, name=f"x_{film_id}_{market_id}")
         for film_id, market_id, _ in film_market_data}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total gross revenue
    model.setObjective(gp.quicksum(low_estimate * x[(film_id, market_id)]
                                   for film_id, market_id, low_estimate in film_market_data), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    # Budget constraint
    model.addConstr(gp.quicksum(low_estimate * x[(film_id, market_id)]
                                for film_id, market_id, low_estimate in film_market_data) <= B, name="budget_limit")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for film_id, market_id, _ in film_market_data:
            if x[(film_id, market_id)].x > 1e-6:
                print(f"Film {film_id} distributed in Market {market_id}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
film_rank_optimization()