"""BYL Mortgage Payment Scenarios v1: deterministic, standard-library Python3.
Run the published script in a writable directory to recreate CSV and JSON.
Not current rates,APR disclosures,loan offers or underwriting results.
"""
from decimal import Decimal,localcontext,ROUND_HALF_UP
from pathlib import Path
import csv,itertools,json
D=Decimal
def scenario(principal,rate,months):
    with localcontext() as ctx:
        ctx.prec=50;p=D(principal);r=D(rate)/1200;n=int(months)
        if p<=0 or r<0 or n<1:raise ValueError('Positive principal/term and nonnegative rate required')
        payment=p/n if r==0 else p*r/(1-(1+r)**(-n));periods=min(60,n)
        remaining=max(D(0),p-payment*periods) if r==0 else max(D(0),p*(1+r)**periods-payment*((1+r)**periods-1)/r)
        return {'payment':payment,'total':payment*n,'interest':payment*n-p,'periods':periods,'remaining':remaining,'principal_paid':p-remaining,'interest_periods':payment*periods-(p-remaining),'paid_periods':payment*periods}
def money(v):return format(D(v).quantize(D('.01'),rounding=ROUND_HALF_UP),'.2f')
def rows():
    data=[]
    for principal,rate,months in itertools.product([100000,200000,300000,400000,500000],[4,5,6,7,8,9],[180,360]):
        s=scenario(principal,rate,months);data.append({'scenario_id':len(data)+1,'loan_principal':money(principal),'nominal_note_rate_percent':str(rate),'term_months':months,'monthly_principal_interest':money(s['payment']),'full_term_principal_interest':money(s['total']),'full_term_interest':money(s['interest']),'comparison_periods':s['periods'],'remaining_principal_after_60':money(s['remaining']),'principal_paid_first_60':money(s['principal_paid']),'interest_paid_first_60':money(s['interest_periods']),'principal_interest_paid_first_60':money(s['paid_periods'])})
    return data
def main():
    dest=Path(__file__).resolve().parent
    if dest.name=='tools':dest=dest.parent/'borrowyourloan.com/data/mortgage'
    dest.mkdir(parents=True,exist_ok=True);data=rows()
    with (dest/'payment-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 Mortgage Payment Scenarios','version':'1.0.0','published':'2026-10-23','canonical_url':'https://www.borrowyourloan.com/resources/mortgage-payment-scenarios','scenario_count':len(data),'methodology':'Decimal precision50. Fixed fully amortizing monthly principal-and-interest payments. Monthly rate equals nominal note interest rate divided by1200 when entered as a percentage. Payment = principal*r/(1-(1+r)^(-n)); at zero rate,payment = principal/n. Remaining principal after60 payments = principal*(1+r)^60-payment*((1+r)^60-1)/r. Principal paid = starting principal minus remaining principal; first60 interest = first60 payments minus principal paid. Full precision is retained; monetary fields round half-up to cents only for publication.','limitations':['These are deterministic mathematical examples,not observed rates,APR disclosures,loan offers or lending/qualification requirements.','Excludes closing costs,discount points,financed fees,mortgage insurance,property taxes,homeowners insurance,HOA,maintenance and other ownership costs.','Fixed note rate,monthly interest,all scheduled payments on time,no prepayment or refinancing. Actual servicing may use different accrual or rounding.','The60-payment balance is not a payoff quote; it excludes payoff-date interest and other applicable charges.','Interest and remaining balance measure different things. A larger payment may reduce principal faster while requiring more monthly cash.','Future insurance,tax,HOA and mortgage-insurance costs are not estimated by this P&I dataset.'],'variables':{'loan_principal':'Starting loan amount; not the home purchase price or cash to close.','nominal_note_rate_percent':'Assumed annual interest rate,not fee-inclusive APR.','term_months':'Number of scheduled monthly payments.','monthly_principal_interest':'Fixed monthly principal-and-interest payment only.','full_term_principal_interest':'Unrounded payment multiplied by term,then rounded for publication.','full_term_interest':'Full-term payments minus starting principal.','remaining_principal_after_60':'Mathematical principal after60 on-time scheduled payments,not a payoff quote.','principal_paid_first_60':'Starting principal minus the60-payment principal balance.','interest_paid_first_60':'First60 P&I payments minus principal paid.','principal_interest_paid_first_60':'Total first60 P&I payments,without taxes,insurance or other ownership costs.'},'rows':data}
    (dest/'payment-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()
