# Complete GUROBIPY implementation - Retry Attempt 4

import gurobipy as gp
from gurobipy import GRB

def optimize_loan_allocation():
    # 1. MODEL & DATA SETUP
    model = gp.Model("loan_optimization")
    
    # Data from the problem context
    customers = [
        {"cust_ID": 1, "credit_score": 720},
        {"cust_ID": 2, "credit_score": 680},
        {"cust_ID": 3, "credit_score": 750}
    ]
    
    loans = [
        {"loan_ID": 101, "amount": 30000, "branch_ID": 1},
        {"loan_ID": 102, "amount": 45000, "branch_ID": 2},
        {"loan_ID": 103, "amount": 25000, "branch_ID": 3},
        {"loan_ID": 104, "amount": 20000, "branch_ID": 1},
        {"loan_ID": 105, "amount": 35000, "branch_ID": 2}
    ]
    
    branch_loans = [
        {"branch_ID": 1, "min_loans": 120000},
        {"branch_ID": 2, "min_loans": 150000},
        {"branch_ID": 3, "min_loans": 100000}
    ]
    
    total_budget = 1000000
    max_loan_per_customer = 50000
    
    # CRITICAL: Validate array lengths before loops
    assert len(customers) == len(loans), "Array length mismatch"
    
    # 2. VARIABLES
    # Create a dictionary of decision variables for loan amounts
    x = {loan["loan_ID"]: model.addVar(vtype=GRB.CONTINUOUS, name=f"x_{loan['loan_ID']}", lb=0, ub=max_loan_per_customer)
         for loan in loans}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total credit score of the loan portfolio
    model.setObjective(gp.quicksum(customers[i]["credit_score"] * x[loans[i]["loan_ID"]] for i in range(len(loans))), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    # Total Budget Constraint
    model.addConstr(gp.quicksum(x[loan["loan_ID"]] for loan in loans) <= total_budget, name="total_budget")
    
    # Branch Minimum Loan Requirement
    for branch in branch_loans:
        model.addConstr(
            gp.quicksum(x[loan["loan_ID"]] for loan in loans if loan["branch_ID"] == branch["branch_ID"]) >= branch["min_loans"],
            name=f"min_loans_branch_{branch['branch_ID']}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for loan in loans:
            loan_id = loan["loan_ID"]
            if x[loan_id].x > 1e-6:
                print(f"Loan ID {loan_id}: {x[loan_id].x:.2f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")

# Run the optimization
optimize_loan_allocation()