#!/usr/bin/env python3
"""
Gurobipy Implementation for Loan Allocation Optimization
"""

import gurobipy as gp
from gurobipy import GRB

def loan_allocation_optimization():
    # 1. MODEL & DATA SETUP
    model = gp.Model("loan_allocation")
    
    # Data from the provided tables
    loan_amounts = [5000.0, 10000.0, 7500.0]  # Example loan amounts
    no_of_customers = [150, 250, 200]  # Number of customers per branch
    credit_scores = [720, 780, 750]  # Credit scores of customers
    
    # Constants
    max_loan_per_customer = 15000
    max_loan_per_credit_score_unit = 600
    max_total_loan_per_customer = 60000
    
    # Validate array lengths
    n_loans = len(loan_amounts)
    n_branches = len(no_of_customers)
    n_customers = len(credit_scores)
    
    assert n_loans == n_branches == n_customers, "Array length mismatch"
    
    # 2. VARIABLES
    # Decision variable: loan amount to be disbursed for each loan
    amount = {i: model.addVar(vtype=GRB.CONTINUOUS, name=f"amount_{i}", lb=0) 
              for i in range(n_loans)}
    
    # 3. OBJECTIVE FUNCTION
    # Maximize the total loan amount disbursed
    model.setObjective(gp.quicksum(amount[i] for i in range(n_loans)), GRB.MAXIMIZE)
    
    # 4. CONSTRAINTS
    
    # Constraint 1: Branch Customer Capacity
    for k in range(n_branches):
        model.addConstr(
            gp.quicksum(amount[i] for i in range(n_loans) if i == k) <= no_of_customers[k] * max_loan_per_customer,
            name=f"branch_capacity_{k}"
        )
    
    # Constraint 2: Customer Credit Score
    for i in range(n_loans):
        model.addConstr(
            amount[i] <= credit_scores[i] * max_loan_per_credit_score_unit,
            name=f"credit_score_limit_{i}"
        )
    
    # Constraint 3: Total Loan Exposure per Customer
    for m in range(n_customers):
        model.addConstr(
            gp.quicksum(amount[i] for i in range(n_loans) if i == m) <= max_total_loan_per_customer,
            name=f"total_exposure_{m}"
        )
    
    # 5. SOLVING & RESULTS
    model.optimize()
    
    if model.status == GRB.OPTIMAL:
        print(f"Optimal value: {model.objVal}")
        for i in range(n_loans):
            if amount[i].x > 1e-6:
                print(f"amount[{i}] = {amount[i].x:.3f}")
    elif model.status == GRB.INFEASIBLE:
        print("Problem is infeasible")
    elif model.status == GRB.UNBOUNDED:
        print("Problem is unbounded")
    
    return model

# Run the optimization
if __name__ == "__main__":
    loan_allocation_optimization()