# Complete GUROBIPY implementation

import gurobipy as gp
from gurobipy import GRB

def optimize_template_assignment():
    """Optimize the assignment of templates to documents to minimize total cost."""
    
    # 1. MODEL & DATA SETUP
    model = gp.Model("cre_Doc_Template_Mgt")
    
    # Data from the problem context
    template_ids = [1, 2, 3]
    document_ids = [101, 102, 103]
    costs = {1: 50.0, 2: 75.0, 3: 100.0}
    
    # Validate array lengths
    assert len(template_ids) == len(costs), "Array length mismatch for templates and costs"
    
    # 2. VARIABLES
    # Decision variables
    x = model.addVars(document_ids, template_ids, vtype=GRB.BINARY, name="x")
    y = model.addVars(template_ids, vtype=GRB.BINARY, name="y")
    
    # 3. OBJECTIVE FUNCTION
    # Minimize the total cost of templates used
    model.setObjective(gp.quicksum(costs[j] * y[j] for j in template_ids), GRB.MINIMIZE)
    
    # 4. CONSTRAINTS
    
    # Each document is assigned to exactly one template
    model.addConstrs((gp.quicksum(x[i, j] for j in template_ids) == 1 for i in document_ids), name="assign_one_template")
    
    # Link template usage to document assignment
    model.addConstrs((x[i, j] <= y[j] for i in document_ids for j in template_ids), name="link_usage")
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in document_ids:
            for j in template_ids:
                if x[i, j].x > 1e-6:
                    print(f"Document {i} is assigned to Template {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_template_assignment()