"""BYL Debt Consolidation Cost Scenarios v1. Deterministic, standard-library only.
Run with Python 3; writes CSV/JSON beside this script when published as reproduce-v1.py.
These are fixed-rate mathematical scenarios, not sampled rates, APR quotes or offers.
"""
from decimal import Decimal, localcontext, ROUND_HALF_UP
from pathlib import Path
import csv,json,itertools
D=Decimal
def payment(principal,annual_rate,months):
    with localcontext() as ctx:
        ctx.prec=45;r=D(annual_rate)/1200
        return D(principal)/months if not r else D(principal)*r/(1-(1+r)**(-months))
def payoff(balance,annual_rate,monthly):
    with localcontext() as ctx:
        ctx.prec=45;b=D(balance);r=D(annual_rate)/1200;p=D(monthly)
        if b<=0 or p<=b*r:raise ValueError('Balance must be positive and payment must exceed first-month interest')
        total=D(0);months=0
        while b>D('0.000000000000000000000001'):
            due=b*(1+r);paid=min(p,due);b=due-paid;total+=paid;months+=1
            if months>1200:raise ValueError('Payoff exceeds 100 years')
        return months,total
def money(v):return format(D(v).quantize(D('.01'),rounding=ROUND_HALF_UP),'.2f')
def rows():
    out=[]
    for balance,pct,rate,term,fee in itertools.product([5000,12000,25000],['2.5','3','4'],[10,15,20,25],[36,60],[0,3,6]):
        with localcontext() as ctx:
            ctx.prec=45;oldp=D(balance)*D(pct)/100;oldmonths,oldtotal=payoff(balance,24,oldp);gross=D(balance)/(1-D(fee)/100);newp=payment(gross,rate,term);newtotal=newp*term
            out.append({'scenario_id':len(out)+1,'debt_balance':money(balance),'current_nominal_annual_rate_percent':'24','current_fixed_monthly_payment':money(oldp),'current_payoff_months':oldmonths,'current_remaining_repayment':money(oldtotal),'new_nominal_annual_rate_percent':str(rate),'new_term_months':term,'withheld_fee_percent':str(fee),'gross_new_principal':money(gross),'withheld_fee_amount':money(gross-D(balance)),'net_debt_payoff_proceeds':money(balance),'new_monthly_payment':money(newp),'new_total_repayment':money(newtotal),'new_interest_excluding_fee':money(newtotal-gross),'new_financing_cost_including_fee':money(newtotal-D(balance)),'monthly_payment_relief':money(oldp-newp),'new_minus_current_repayment':money(newtotal-oldtotal)})
    return out
def main():
    dest=Path(__file__).resolve().parent
    if dest.name=='tools':dest=dest.parent/'borrowyourloan.com/data/debt-consolidation'
    dest.mkdir(parents=True,exist_ok=True);data=rows()
    with (dest/'cost-scenarios-v1.csv').open('w',newline='',encoding='utf-8') as f:
        w=csv.DictWriter(f,fieldnames=list(data[0]),lineterminator='\n');w.writeheader();w.writerows(data)
    meta={'title':'BYL Debt Consolidation Cost Scenarios','version':'1.0.0','published':'2026-10-23','canonical_url':'https://www.borrowyourloan.com/resources/debt-consolidation-cost-scenarios','scenario_count':len(data),'methodology':'Decimal precision 45. Existing debt is one mathematical balance at fixed 24% nominal annual interest, paid monthly with a fixed dollar payment until a smaller final payment. New loan proceeds equal the debt balance after a fee withheld as a percentage of gross principal. New gross principal = debt balance / (1 - fee fraction). Payments use monthly amortization with nominal annual rate / 12. Calculations retain full precision; monetary fields round half-up to cents only for publication. New total repayment minus current remaining repayment compares future dollars from today.','limitations':['Not observed market data, current rate quotes, lender APR disclosures, eligibility guidance or offers.','Existing payment is fixed in dollars; it is not a declining credit-card minimum-payment model.','Equal-rate aggregate debt model; accounts with different rates or dates need a separate account-level calculation.','Monthly interest model excludes daily accrual, account reuse, late fees, missed payments, taxes and optional insurance.','A financed withheld fee increases principal. New nominal interest rate is not fee-inclusive APR; no fee-inclusive APR is calculated.','Negative new-minus-current repayment indicates lower modeled future repayment; it does not establish suitability or guaranteed savings.'],'variables':{'monthly_payment_relief':'Current fixed monthly payment minus new payment; positive means lower new payment.','new_minus_current_repayment':'New total repayment minus current remaining repayment; negative means lower modeled future repayment.','new_financing_cost_including_fee':'New total repayment minus net proceeds used to pay current debt.','current_payoff_months':'Whole payment periods including any smaller final payment.'},'rows':data}
    (dest/'cost-scenarios-v1.json').write_text(json.dumps(meta,ensure_ascii=False,indent=2)+'\n',encoding='utf-8');print(json.dumps({'scenarios':len(data),'output':str(dest)}))
if __name__=='__main__':main()
