import requests
import re
import pandas as pd
import asyncio
import pyodide_http
from pyodide.http import pyfetch, open_url
from urllib.parse import quote
from pyscript import document, window
from requests.exceptions import HTTPError
from datetime import datetime, timezone, timedelta


pyodide_http.patch_all()


def pcs_df(loans):

    url =  open_url('https://4412874.fs1.hubspotusercontent-na1.net/hubfs/4412874/LoanPricingCalculator/loans_client_db_prod_data_06272025.csv')
    df = pd.read_csv(url)
    
    loan_types = list(pd.unique(df['Loan Type']))
    loan_code = list(pd.unique(df['Loan Product']))

    loan_dict = dict(zip(loan_code, loan_types))

    user_choice = loans

    if user_choice in loan_dict:
        selected_value = loan_dict[user_choice]       
        pcs_df = df[df['Loan Type'] == selected_value]
    
    return pcs_df

def printPDF(event):

    printPDF = document.getElementById("printPDF")
    printPDF.style.display = "none"

    title_section = document.getElementById("title_section")
    title_section.style.display = "block"

    window.print()

    printPDF = document.getElementById("printPDF")
    printPDF.style.display = "block"

    title_section = document.getElementById("title_section")
    title_section.style.display = "none"


def clear(event):

    document.getElementById('InstLoanName').value = ''
    document.getElementById('InstCapRatio').value = ''
    document.getElementById('AnnlGrowth').value = ''
    document.getElementById('Balance').value = ''
    document.getElementById('AnnlRate').value = ''
    document.getElementById('Term').value = ''
    document.getElementById('LLP').value = ''
    document.getElementById('Decay').value = ''
    

def clear_all(event):

    document.getElementById('InstLoanName').value = ''
    document.getElementById('InstCapRatio').value = ''
    document.getElementById('AnnlGrowth').value = ''
    document.getElementById('Balance').value = ''
    document.getElementById('AnnlRate').value = ''
    document.getElementById('Term').value = ''
    document.getElementById('LLP').value = ''
    document.getElementById('Decay').value = ''

    emailPrompt = document.querySelector("#emailPrompt")
    CalcPrompt = document.querySelector("#CalcPrompt")
    BaseROA = document.querySelector("#BaseROA")
    WAL = document.querySelector("#WAL")
    IntInc = document.querySelector("#IntInc")
    IntIncPer = document.querySelector("#IntIncPer")
    NII = document.querySelector("#NII")
    NIIPer = document.querySelector("#NIIPer")
    TotRev = document.querySelector("#TotRev")
    TotRevPer = document.querySelector("#TotRevPer")
    COF = document.querySelector("#COF")
    COFPer = document.querySelector("#COFPer")
    OrigCost = document.querySelector("#OrigCost")
    OrigCostPer = document.querySelector("#OrigCostPer")
    NonCrSevcg = document.querySelector("#NonCrSevcg")
    NonCrSevcgPer = document.querySelector("#NonCrSevcgPer")
    CrSevcg = document.querySelector("#CrSevcg")
    CrSevcgPer = document.querySelector("#CrSevcgPer")
    AnnlLLP = document.querySelector("#AnnlLLP")
    AnnlLLPPer = document.querySelector("#AnnlLLPPer")
    Ovhd = document.querySelector("#Ovhd")
    OvhdPer = document.querySelector("#OvhdPer")
    TotExp = document.querySelector("#TotExp")
    TotExpPer = document.querySelector("#TotExpPer")
    ROA = document.querySelector("#ROA")
    ROAPer = document.querySelector("#ROAPer")
    RateAdj = document.querySelector("#RateAdj")
    RateAdjPer = document.querySelector("#RateAdjPer")
    TargetROA = document.querySelector("#TargetROA")
    TargetROAPer = document.querySelector("#TargetROAPer")
    OrgLnRate = document.querySelector("#OrgLnRate")
    RAROA = document.querySelector("#RAROA")
    ROALnRate = document.querySelector("#ROALnRate")
    PresentValue = document.querySelector("#PresentValue")
    PresentValueLabel = document.querySelector("#PresentValueLabel")
    GainLoss = document.querySelector('#GainLoss')
    GainLossLabel = document.querySelector('#GainLossLabel')
    finalTable = document.getElementById("finalTable")
    finalTable.style.display = "none"
    finalTable2 = document.getElementById("finalTable2")
    finalTable2.style.display = "none"
    printPDF = document.getElementById("printPDF")
    printPDF.style.display = "none"

    emailPrompt.innerText = " "
    CalcPrompt.innerText = " "
    BaseROA.innerText = " "
    WAL.innerText = " "
    IntInc.innerText = " "
    IntIncPer.innerText = " "
    NII.innerText = " "
    NIIPer.innerText = " "
    TotRev.innerText = " "
    TotRevPer.innerText = " "
    COF.innerText = " "
    COFPer.innerText = " "
    OrigCost.innerText = " "
    OrigCostPer.innerText = " "
    NonCrSevcg.innerText = " "
    NonCrSevcgPer.innerText = " "
    CrSevcg.innerText = " "
    CrSevcgPer.innerText = " "
    AnnlLLP.innerText = " "
    AnnlLLPPer.innerText = " "
    Ovhd.innerText = " "
    OvhdPer.innerText = " "
    TotExp.innerText = " "
    TotExpPer.innerText = " "
    ROA.innerText = " "
    ROAPer.innerText = " "
    RateAdj.innerText = " "
    RateAdjPer.innerText = " "
    TargetROA.innerText = " "
    TargetROAPer.innerText = " "
    OrgLnRate.innerText = " "
    RAROA.innerText = " "
    ROALnRate.innerText = " "
    PresentValue.innerText = " "
    PresentValueLabel.innerText = " "
    GainLoss.innerText = " "
    GainLossLabel.innerText = " "


##### CALCULATIONS SECTION

def calcBaseROA(InstCapRatio, AnnlGrowth):

    BaseROA = (InstCapRatio) * (AnnlGrowth)
    result = document.querySelector("#BaseROA")
    result.setAttribute("style", "color:black")
    result.innerText = f'{(BaseROA*100):.2f} %'

    return BaseROA

def calcWAL(term_months, annual_rate_percent, annual_cpr_percent):

    if term_months == 0:
        term_months = 0.0000001
    if annual_rate_percent == 0:
        annual_rate_percent = 0.0000001

    """
    Calculates Weighted Average Life (WAL) for a fixed amortizing loan with prepayments.

    Parameters:
        term_months (int): Number of loan periods (months)
        annual_rate_percent (float): Annual interest rate in percent (e.g., 6.0 for 6%)
        annual_cpr_percent (float): Annual CPR in percent (e.g., 5.0 for 5%)

    Returns:
        float: WAL in years
    """
    # Convert to decimals and monthly rates
    r = annual_rate_percent / 12        # Monthly interest rate
    cpr = annual_cpr_percent
    smm = 1 - (1 - cpr) ** (1 / 12)           # Convert CPR to SMM
    survive = 1 - smm
    discount = 1 / (1 + r)

    # WAL closed-form approximation from VBA
    wal1 = (1 - survive ** term_months) / smm
    wal2 = discount * (discount ** term_months - survive ** term_months) / (survive - discount)
    wal3 = 1 - discount ** term_months

    wal_months = (wal1 + wal2) / wal3
    wal_years = wal_months / 12

    result = document.querySelector("#WAL")
    result.setAttribute("style", "color:black")
    result.innerText = f"{wal_years:.2f}"

    return wal_years


def calcIntInc(Balance, AnnlRate, loans):

    if loans == "Ln1":
        ratio = 0.85
        AnnlRate = AnnlRate * ratio
        IntInc = Balance * AnnlRate
        IntIncPer = AnnlRate

    elif loans == "Ln2":
        ratio = 0.73
        AnnlRate = AnnlRate * ratio
        IntInc = Balance * AnnlRate
        IntIncPer = AnnlRate
        
    else:
        IntInc = Balance * AnnlRate
        IntIncPer = IntInc/Balance

    result = document.querySelector("#IntInc")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(IntInc, 0))}"
    per_result = document.querySelector("#IntIncPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(IntIncPer*100):.2f} %"

    return IntInc

def calcNII(loans, Balance):

    df = pcs_df(loans)
    NII = df.loc[df['Sub-Name'] == 'Non-Interest Income', 'Client Avg.'].values[0]
    NIIPer = NII/Balance
    result = document.querySelector("#NII")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(NII, 0))}"
    per_result = document.querySelector("#NIIPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(NIIPer*100):.2f} %"

    return NII

def calcTotRev(CalcdIntInc, CalcdNII, Balance):

    IntInc  = CalcdIntInc
    NII = CalcdNII

    TotRev = NII + IntInc
    TotRevPer = TotRev/Balance

    result = document.querySelector("#TotRev")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(TotRev, 0))}"
    per_result = document.querySelector("#TotRevPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(TotRevPer*100):.2f} %"

    return TotRev, TotRevPer

def calcCOF(CalcdWAL, Balance, loans, term):

    if loans == "Ln12":
        CalcdWAL = 1
    
    df = treasury_data_df
    filtered = df[df['months'] < term]

    if not filtered.empty:
        selected_row = filtered.loc[filtered['months'].idxmax()]
    else:
        selected_row = None  # or handle the "no match" case

    if loans == "Ln1" or loans == "Ln2" or loans == "Ln10":

        COFPer = (prime - 4.14)/100
        COF = Balance * COFPer

    else:

        COFPer = selected_row.loc['yield_percent']
        COFPer = COFPer/100
        COF = Balance * COFPer

    result = document.querySelector("#COF")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(COF, 0))}"
    per_result = document.querySelector("#COFPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(COFPer*100):.2f} %"

    return COF

def calcPCSdata(loans, Balance, CalcdWAL):

    df = pcs_df(loans)
    OrigCost = df.loc[df['Sub-Name'] == 'Origination Costs', 'Client Avg.'].values[0] / CalcdWAL
    NonCrSevcg = df.loc[df['Sub-Name'] == 'Non-Credit Related Servicing', 'Client Avg.'].values[0]
    CrSevcg = df.loc[df['Sub-Name'] == 'Credit Realtred Servicing', 'Client Avg.'].values[0]
    Ovhd = df.loc[df['Sub-Name'] == 'Overhead', 'Client Avg.'].values[0]

    OrigCostResult = document.querySelector("#OrigCost")
    OrigCostResult.setAttribute("style", "color:black")
    OrigCostResult.innerText = f"$ {'{:,.0f}'.format(round(OrigCost, 0))}"
    NonCrSevcgResult = document.querySelector("#NonCrSevcg")
    NonCrSevcgResult.setAttribute("style", "color:black")
    NonCrSevcgResult.innerText = f"$ {'{:,.0f}'.format(round(NonCrSevcg, 0))}"
    CrSevcgResult = document.querySelector("#CrSevcg")
    CrSevcgResult.setAttribute("style", "color:black")
    CrSevcgResult.innerText = f"$ {'{:,.0f}'.format(round(CrSevcg, 0))}"
    OvhdResult = document.querySelector("#Ovhd")
    OvhdResult.setAttribute("style", "color:black")
    OvhdResult.innerText = f"$ {'{:,.0f}'.format(round(Ovhd, 0))}"

    OrigCostPer = OrigCost/Balance
    NonCrSevcgPer = NonCrSevcg/Balance
    CrSevcgPer = CrSevcg/Balance
    OvhdPer = Ovhd/Balance

    OrigCost_per = document.querySelector("#OrigCostPer")
    OrigCost_per.setAttribute("style", "color:black")
    OrigCost_per.innerText = f"{(OrigCostPer*100):.2f} %"
    NonCrSevc_per = document.querySelector("#NonCrSevcgPer")
    NonCrSevc_per.setAttribute("style", "color:black")
    NonCrSevc_per.innerText = f"{(NonCrSevcgPer*100):.2f} %"
    CrSevcg_per = document.querySelector("#CrSevcgPer")
    CrSevcg_per.setAttribute("style", "color:black")
    CrSevcg_per.innerText = f"{(CrSevcgPer*100):.2f} %"
    Ovhd_per = document.querySelector("#OvhdPer")
    Ovhd_per.setAttribute("style", "color:black")
    Ovhd_per.innerText = f"{(OvhdPer*100):.2f} %"

    return OrigCost, NonCrSevcg, CrSevcg, Ovhd

def calcAnnlLLP(Balance, LLP):

    AnnlLLP = Balance * LLP
    AnnlLLPPer = LLP

    result = document.querySelector("#AnnlLLP")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(AnnlLLP, 0))}"  

    per_result = document.querySelector("#AnnlLLPPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(AnnlLLPPer*100):.2f} %"

    return AnnlLLP

def calcTotExp(Balance, COF, OrigCost, NonCrSevcg, CrSevcg, Ovhd, AnnlLLP):

    TotExp = COF + OrigCost + NonCrSevcg + CrSevcg + Ovhd + AnnlLLP
    TotExpPer = TotExp / Balance

    result = document.querySelector("#TotExp")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(TotExp, 0))}"  

    per_result = document.querySelector("#TotExpPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(TotExpPer*100):.2f} %"

    return TotExp, TotExpPer

def calcROA(CalcdTotRev, CalcdTotExp):

    TotRev, TotRevPer = CalcdTotRev
    TotExp, TotExpPer = CalcdTotExp
    ROA = TotRev - TotExp
    ROAPer = TotRevPer - TotExpPer

    result = document.querySelector("#ROA")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(ROA, 0))}"  

    per_result = document.querySelector("#ROAPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(ROAPer*100):.2f} %"

    return ROA, ROAPer

def calcRateAdj(Balance, BaseROA, CalcdROA):

    ROA, ROAPer = CalcdROA
    RateAdjPer = BaseROA - ROAPer ## io_rate for PV Variable Rate
    RateAdj = Balance * RateAdjPer

    result = document.querySelector("#RateAdj")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(RateAdj, 0))}"  

    per_result = document.querySelector("#RateAdjPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(RateAdjPer*100):.2f} %"

    return RateAdj, RateAdjPer

def calcTargetROA(Balance, BaseROA):

    TargetROAPer = BaseROA
    TargetROA = Balance * TargetROAPer

    result = document.querySelector("#TargetROA")
    result.setAttribute("style", "color:black")
    result.innerText = f"$ {'{:,.0f}'.format(round(TargetROA, 0))}"  

    per_result = document.querySelector("#TargetROAPer")
    per_result.setAttribute("style", "color:black")
    per_result.innerText = f"{(TargetROAPer*100):.2f} %"

    return TargetROA

def calcROALnRate(AnnlRate, calcdRateAdj):

    RateAdj, RateAdjPer = calcdRateAdj
    OrgLnRate = AnnlRate
    RAROA = RateAdjPer
    ROALnRate = OrgLnRate + RAROA ## Discount Rate for PV Variable Rate

    per_OrgLnRate = document.querySelector("#OrgLnRate")
    per_OrgLnRate.setAttribute("style", "color:#4472C4; font-size: 25px")
    per_OrgLnRate.innerText = f"{(OrgLnRate*100):.2f} %"

    per_RAROA = document.querySelector("#RAROA")
    per_RAROA.setAttribute("style", "color:#4472C4; font-size: 25px")
    per_RAROA.innerText = f"{(RAROA*100):.2f} %"

    per_ROALnRate = document.querySelector("#ROALnRate")
    per_ROALnRate.setAttribute("style", "color:#4472C4; font-size: 25px")
    per_ROALnRate.innerText = f"{(ROALnRate*100):.2f} %"

    return ROALnRate


def calcPresentValue(rate, term_months, CPR, discount_rate, balance, loans, io_rate):

    if rate == 0:
        rate = 0.000001

    if loans not in ("Ln1", "Ln2", "Ln10", "Ln12"):
        """
        Calculate the present value of a bond or loan, accounting for the effects of prepayments on principal reduction,
        recalculating the payment after each prepayment, and discounting.

        Parameters:
        rate (float): Interest rate of the loan or bond (annualized).
        term_months (int): Total term of the loan or bond in months.
        CPR (float): Conditional Prepayment Rate (annualized).
        discount_rate (float): Discount rate (annualized).

        Returns:
        float: The present value considering prepayments.
        """
        # Convert the annual interest rate and discount rate to monthly rates
        monthly_rate = rate / 12
        monthly_discount_rate = discount_rate / 12
        
        # Convert CPR to SMM
        SMM = 1 - (1 - CPR) ** (1 / 12)
        
        # Initialize present value and starting principal
        present_value = 0.0
        outstanding_principal = 1.0  # Assuming a $1 principal for simplicity
        
        for t in range(1, term_months + 1):
            # Calculate the interest payment based on the remaining principal
            interest_payment = outstanding_principal * monthly_rate
            
            # Calculate the prepayment for this period (applied at the beginning of the period)
            prepayment = outstanding_principal * SMM
            
            # Update the outstanding principal after the prepayment
            outstanding_principal -= prepayment
            
            # Recalculate the monthly payment based on the new outstanding principal and remaining term
            remaining_term = term_months - t + 1
            if remaining_term > 0:
                new_payment = (outstanding_principal * monthly_rate * (1 + monthly_rate) ** remaining_term) / \
                            ((1 + monthly_rate) ** remaining_term - 1)
            else:
                new_payment = 0
            
            # Calculate the principal repayment for this period
            principal_payment = new_payment - interest_payment
            
            # Update the outstanding principal after the principal repayment
            outstanding_principal -= principal_payment
            
            # Total payment for the period
            total_payment = interest_payment + principal_payment + prepayment
            
            # Discount the adjusted payment to present value
            discounted_payment = total_payment / (1 + monthly_discount_rate) ** t
            present_value += discounted_payment

            # Handle edge case where principal fully repaid
            if outstanding_principal <= 0:
                break
        
        present_value = present_value*100
        gain_loss = abs(balance * (1 - present_value/100)) 

        PresentValueLabel = document.querySelector("#PresentValueLabel")
        PresentValueLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
        PresentValueLabel.innerText = f"The RAROA present value is: "

        PresentValue = document.querySelector("#PresentValue")
        PresentValue.setAttribute("style", "color:#2d2f2f; font-size: 22px")
        PresentValue.innerText = f"{(present_value):.2f} %"


        if discount_rate < rate:

            GainLossLabel = document.querySelector("#GainLossLabel")
            GainLossLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLossLabel.innerHTML = f"Selling this loan today could result in a <span style='text-decoration: underline; color: green;'>GAIN</span> of: "

            GainLoss = document.querySelector("#GainLoss")
            GainLoss.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLoss.innerHTML = f"$ {abs(gain_loss):.0f}"

        else:

            GainLossLabel = document.querySelector("#GainLossLabel")
            GainLossLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLossLabel.innerHTML = f"Selling this loan today could result in a <span style='text-decoration: underline; color: red;'>LOSS</span> of: "

            GainLoss = document.querySelector("#GainLoss")
            GainLoss.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLoss.innerHTML = f"$ {abs(gain_loss):.0f}"


    elif loans in ("Ln1", "Ln2", "Ln10", "Ln12"):
        """
        Calculate the value of an Interest-Only (IO) strip.

        Parameters:
        - io_rate: The interest rate on the IO strip (as a decimal, e.g., 0.02 for 2%)
        - cpr: Constant Prepayment Rate (as a decimal, e.g., 0.10 for 10%)
        - periods: Number of periods (months)
        - principal_balance: Initial principal balance

        Returns:
        - The value of the IO strip.
        """
        pv_io = 0.0
        smm = 1 - (1 - CPR) ** (1 / 12)
        
        for t in range(1, term_months + 1):
            # Calculate the remaining principal balance after prepayment
            remaining_principal = balance * (1 - smm) ** t
            
            # Interest payment for the period
            interest_payment = remaining_principal * io_rate / 12
            
            # Discount the interest payment back to present value
            pv_io += interest_payment / (1 + discount_rate / 12) ** t

        io_price = (balance + pv_io) / balance *100

        ## OLD TEXT OUTPUT
        """
        PresentValueLabel = document.querySelector("#PresentValueLabel")
        PresentValueLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
        PresentValueLabel.innerText = f"Price of the variable rate instrument: "
        
        PresentValue = document.querySelector("#PresentValue")
        PresentValue.setAttribute("style", "color:#2d2f2f; font-size: 22px")
        PresentValue.innerText = f"{io_price:.2f} %"

        GainLossLabel = document.querySelector("#GainLossLabel")
        GainLossLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
        GainLossLabel.innerHTML = f"Present Value of the marginal interest: "

        if pv_io > 0:
            GainLoss = document.querySelector("#GainLoss")
            GainLoss.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLoss.innerHTML = f"<span style='color: green;'>$ {(pv_io):,.0f}</span>"
        else:
            GainLoss = document.querySelector("#GainLoss")
            GainLoss.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLoss.innerHTML = f"<span style='color: red;'>($ {abs(pv_io):,.0f})</span>"     
        """

        PresentValueLabel = document.querySelector("#PresentValueLabel")
        PresentValueLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
        PresentValueLabel.innerText = f"The RAROA present value is: "
        
        PresentValue = document.querySelector("#PresentValue")
        PresentValue.setAttribute("style", "color:#2d2f2f; font-size: 22px")
        PresentValue.innerText = f"{io_price:.2f} %"


        if discount_rate < rate:

            GainLossLabel = document.querySelector("#GainLossLabel")
            GainLossLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLossLabel.innerHTML = f"Selling this loan today could result in a <span style='text-decoration: underline; color: green;'>GAIN</span> of: "

            GainLoss = document.querySelector("#GainLoss")
            GainLoss.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLoss.innerHTML = f"$ {abs(pv_io):,.0f}"

        else:

            GainLossLabel = document.querySelector("#GainLossLabel")
            GainLossLabel.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLossLabel.innerHTML = f"Selling this loan today could result in a <span style='text-decoration: underline; color: red;'>LOSS</span> of: "

            GainLoss = document.querySelector("#GainLoss")
            GainLoss.setAttribute("style", "color:#2d2f2f; font-size: 22px")
            GainLoss.innerHTML = f"$ {abs(pv_io):,.0f}"





##### CALCULATIONS SECTION


######################### TREASURY DATA SECTION


async def invoke_treasury_data_lambda():

    treasury_data_url = 'https://inczvecoy2xv76eolih6xp4s340ribbg.lambda-url.ap-southeast-2.on.aws/'

    while True:
        try:
            # Make a GET request to the Lambda function URL
            response = requests.get(treasury_data_url)
            response.raise_for_status()  # Raises an HTTPError for bad responses (4xx and 5xx)
            
            if response.status_code == 200:
            # Try to parse JSON
                try:
                    response_json = response.json()
                    print('Lambda Response Status:', response.status_code)
                    data = response_json
                    break
                
                except ValueError:
                    print('Lambda Response Status:', response.status_code)
                    data = response.text
                    break

            await asyncio.sleep(5)
        
        except HTTPError:
            pass

    return data


def setDf(data):
    
    df = pd.DataFrame(data)
    df = df.rename(columns=lambda name: name.replace(' Month', 'm').replace(' Yr', 'y').replace(' Mo', 'm').lower())
    df['date'] = pd.to_datetime(df['date'])

    today_utc = (datetime.now(timezone.utc) - timedelta(days=2)).date()
    filtered_df = df[df['date'].dt.date == today_utc]

    # Melt to long format including the 7y point
    df_rates = filtered_df.melt(
        id_vars='date',
        value_vars=['1m', '3m', '6m', '1y', '2y', '5y', '7y', '10y', '30y'],
        var_name='maturity',
        value_name='yield_percent'
    )

    # Add WAL mapping
    wal_mapping = {
        '1m': 0.08,
        '3m': 0.25,
        '6m': 0.5,
        '1y': 1,
        '2y': 2,
        '5y': 5,
        '7y': 7,
        '10y': 10,
        '30y': 30
    }
    df_rates['WAL'] = df_rates['maturity'].map(wal_mapping)

    # Define and apply order of maturities
    maturity_order = ['1m', '3m', '6m', '1y', '2y', '5y', '7y', '10y', '30y']
    df_rates['maturity'] = pd.Categorical(df_rates['maturity'], categories=maturity_order, ordered=True)
    df_rates = df_rates.sort_values('maturity')
    df_rates['months'] = df_rates['maturity'].apply(
        lambda x: int(x[:-1]) if x.endswith('m') else int(x[:-1]) * 12
    ).astype(int)


    df_rates['yield_percent'] = df_rates['yield_percent'].astype(float)

    return df_rates


######################### TREASURY DATA SECTION

######################### PRIME SECTION


async def invoke_prime_lambda():

    prime_url = 'https://3kifi2rhxjdtehspmauxaejpie0xikya.lambda-url.ap-southeast-2.on.aws/'

    while True:
        try:
            # Make a GET request to the Lambda function URL
            response = requests.get(prime_url)
            response.raise_for_status()  # Raises an HTTPError for bad responses (4xx and 5xx)
            
            if response.status_code == 200:
            # Try to parse JSON
                try:
                    response_json = response.json()
                    print('Lambda Response Status:', response.status_code)
                    data = response_json
                    break
                
                except ValueError:
                    print('Lambda Response Status:', response.status_code)
                    data = response.text
                    break

            await asyncio.sleep(5)

        except HTTPError:
            pass

    return data

def getPrime(data):

    value = data['observations'][0]['value']

    return value

######################### PRIME SECTION

def email_submit(event):

    email_address = document.getElementById("email").value
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    emailPrompt = document.querySelector("#emailPrompt")
    CalcPrompt = document.querySelector("#CalcPrompt")

    if re.match(pattern, email_address) is not None:
        email = True
        document.getElementById('calculate').removeAttribute("disabled")
        displayText = " "
        emailPrompt.innerText = displayText
        CalcPrompt.innerText = displayText

        event.target.style.backgroundColor = '#009900'

    else:
        displayText = "Please enter a valid email address"
        emailPrompt.setAttribute("style", "color:red")
        emailPrompt.innerText = displayText
        email = False
        document.getElementById('calculate').setAttribute("disabled", True)

    global confirm
    confirm = True

    return email


def emailCheck(email):
    
    pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
    if re.match(pattern, email) is not None:

        emailPrompt = document.querySelector("#emailPrompt")
        emailPrompt.innerText = " "
        email = True

    else:
        emailPrompt = document.querySelector("#emailPrompt")
        displayText = "Please enter a valid email address"
        emailPrompt.setAttribute("style", "color:red")
        emailPrompt.innerText = displayText

        CalcPrompt = document.querySelector("#CalcPrompt")
        displayText = "Kindly confirm that all fields have been filled in correctly or are valid. (email)"
        CalcPrompt.setAttribute("style", "color:red")
        CalcPrompt.innerText = displayText

        email = False

    return email

def calculate(event):

    CalcPrompt = document.querySelector("#CalcPrompt")
    CalcPrompt.innerText = " "

    email = document.getElementById("email").value

    try:
            
        if emailCheck(email) == True and confirm == True:
            try:
                InstLoanName = document.getElementById('InstLoanName').value
                loans = document.querySelector("#loans").value
                InstCapRatio = float(document.querySelector("#InstCapRatio").value)/100
                AnnlGrowth = float(document.querySelector("#AnnlGrowth").value)/100
                Balance = float(document.querySelector("#Balance").value)
                AnnlRate = float(document.querySelector("#AnnlRate").value)/100
                Term = int(document.querySelector("#Term").value)
                LLP = float(document.querySelector("#LLP").value)/100
                Decay = float(document.querySelector("#Decay").value)/100

                BaseROA = calcBaseROA(InstCapRatio, AnnlGrowth)
                WAL = calcWAL(Term, AnnlRate, Decay)
                IntInc = calcIntInc(Balance, AnnlRate, loans)
                NII = calcNII(loans, Balance)
                TotRev = calcTotRev(IntInc, NII, Balance)
                COF = calcCOF(WAL, Balance, loans, Term)
                OrigCost, NonCrSevcg, CrSevcg, Ovhd = calcPCSdata(loans, Balance, WAL)
                AnnlLLP = calcAnnlLLP(Balance, LLP)
                TotExp = calcTotExp(Balance, COF, OrigCost, NonCrSevcg, CrSevcg, Ovhd, AnnlLLP)
                ROA = calcROA(TotRev, TotExp)
                RateAdj = calcRateAdj(Balance, BaseROA, ROA)
                TargetROA = calcTargetROA(Balance, BaseROA)
                ROALnRate = calcROALnRate(AnnlRate, RateAdj)
                PresentValue = calcPresentValue(AnnlRate, Term, Decay, ROALnRate, Balance, loans, RateAdj[-1])

                finalTable = document.getElementById("finalTable")
                finalTable.style.display = "table"

                finalTable2 = document.getElementById("finalTable2")
                finalTable2.style.display = "table"

                printPDF = document.getElementById("printPDF")
                printPDF.style.display = "block"

            except ValueError or AttributeError:
                CalcPrompt = document.querySelector("#CalcPrompt")
                displayText = "Kindly confirm that all fields have been filled in correctly or are valid."
                CalcPrompt.setAttribute("style", "color:red")
                CalcPrompt.innerText = displayText

        elif confirm == False:

            CalcPrompt = document.querySelector("#CalcPrompt")
            displayText = "Kindly confirm above that you agree with the following terms and conditions."
            CalcPrompt.setAttribute("style", "color:red")
            CalcPrompt.innerText = displayText

    except ZeroDivisionError:
            CalcPrompt = document.querySelector("#CalcPrompt")
            displayText = "Please double-check your input values."
            CalcPrompt.setAttribute("style", "color:red")
            CalcPrompt.innerText = displayText

    return email

    
async def load_url():
    # Simulating long-running backend operations (e.g., calling APIs)
    ## Treasury Data Section
    treasury_data = await invoke_treasury_data_lambda()

    global treasury_data_df
    treasury_data_df = setDf(treasury_data)

    ## PRIME Section
    prime_data = await invoke_prime_lambda()

    global prime
    prime = float(getPrime(prime_data))


async def main():
    emailPrompt = document.querySelector("#emailPrompt")
    CalcPrompt = document.querySelector("#CalcPrompt")

    # Start the backend loading process asynchronously
    loading_url_task = asyncio.create_task(load_url())

    # Allow UI to be interactive while loading files
    while not loading_url_task.done():
        await asyncio.sleep(5)  # Yield control to the event loop to keep UI responsive

    # Once loading is complete, update the UI
    CalcPrompt.innerText = " "
    emailPrompt.innerText = " "
    document.getElementById('calculate').removeAttribute("disabled")  # Enable the button


# Initial values
treasury_data_df = []
prime = 0
confirm = False

# Function to start the main coroutine after the window has loaded
def on_load():
    # Display initial loading prompt
    emailPrompt = document.querySelector("#emailPrompt")
    displayText = "Gathering real-time market data, this takes about 10-20 seconds."
    emailPrompt.setAttribute("style", "color:red; font-weight:bold")
    emailPrompt.innerText = displayText

    CalcPrompt = document.querySelector("#CalcPrompt")
    CalcPrompt.setAttribute("style", "color:gray")
    CalcPrompt.innerText = "Files are loading, Please wait"

    asyncio.create_task(main())  # Ensure main is run as a coroutine

# Trigger the process when the window has fully loaded
window.addEventListener('load', on_load())



