# calc.py
from datetime import datetime, timedelta
from defines import END_YEAR
from db import get_historical_data

# GLOBAL CONSTANTS
LEASE = 0
ANNOT_LIMIT = 1000

# Dates that don't change
first_pay_date = datetime(2026,1,8)
school_fees_date = datetime(2025, 12, 5)
car_balloon_date = datetime(2026, 11, 15)
mich_present_date = datetime(2026,10,15)
end_date = datetime(END_YEAR, 4, 15)

def bill_amount_today(finance, day, bill_data, bt_id_name, total ):
    amt=0
    day_str = day.strftime("%Y-%m-%d")

    for b in bill_data:
        # there may be more than one bill on this day, keep add amount and keep going in loop
        if b['bill_date'] == day_str:
            amt += b['amount']
            if b['amount'] > ANNOT_LIMIT:
                n=bt_id_name[ b['bill_type'] ]
                print( f"bill_amt_today {n} for {day_str} has amt={b['amount']}" )
                add_annotation(finance, day, total-b['amount'], -b['amount'], f"Pay {n}" )
        # bills are desc order so if the bill is before the day we are after then stop looking
        if b['bill_date'] < day_str:
            return amt
    #failsafe, doubt this even can occur with bills older than today
    return amt

def add_annotation(finance, dt, total, delta, text):
    tm = dt.timestamp() * 1000
    if delta >= 0:
        text += f": ${int(abs(delta))}"
    else:
        text += f": -${int(abs(delta))}"
    finance['annotations'].append( { 'label': text, 'x': tm, 'y': total } )
    return
    
def calculate_savings_depletion(finance, bill_data, bill_type):
    # Extract all the financial data from the database
    D_Salary = finance['D_Salary']
    D_Num_fortnights_pay = finance['D_Num_fortnights_pay']
    School_Fees = finance['School_Fees']
    Car_loan_via_pay = finance['Car_loan_via_pay']
    Car_loan = finance['Car_loan']
    Car_balloon = finance['Car_balloon']
    Car_buyout = finance['Car_buyout']
    Living_Expenses = finance['Living_Expenses']
    Savings = finance['Savings']
    Interest_Rate = finance['Interest_Rate']
    Inflation = finance['Inflation']
    Mich_present = finance['Mich_present']
    Overseas_trip = finance['Overseas_trip']
    Mark_reno = finance['Mark_reno']
    D_leave_owed_in_days = finance['D_leave_owed_in_days']
    Sell_shares = finance['Sell_shares']
    D_TLS_shares = finance['D_TLS_shares']
    M_TLS_shares = finance['M_TLS_shares']
    D_CBA_shares = finance['D_CBA_shares']
    TLS_price = finance['TLS_price']
    CBA_price = finance['CBA_price']
    Ioniq6_future = finance['Ioniq6_future']

    ### COMPLEX tax implications with my leave I have not taken.   It will be taxed in the financial year I 'quit' ###

    # leave in days, 10 business days to a fortnight, 
    # paid before tax I earn $7830.42 / fortnight.  Tax on that will be at 37% or $4933.16 after tax
    #   if we could stretch this to July 2026, then would be more (due to less tax)
    bus_days_in_fortnight=10

    # this is what I now earn before-tax (and I *THINK* vehicle allowance won't be paid X 12 weeks)
    pre_tax_D_earning = 8143.65

    # whenever I quit, I get my leave paid out and I get 12 weeks (or 6 pays) -- notice
    payout = ((D_leave_owed_in_days/bus_days_in_fortnight) + 6) * pre_tax_D_earning

    # just use redundancy calc...
#    payout = 83115.84
    print( f"leave payout gross={payout}" )

    # However, if I quit in the next fin year - tax will be: $4,288 plus 30c for each $1 over $45,000
    #   (assuming the 7830.42 * ~90/bus_days_in_fortnight = ~ $64k - > 45k and < $135k bracket is 30% OR .37c for $135->$190k)
    #      - IF I am close to $190k+, just wait a month and quit in new financial year
    #   Given, I probably can't stop Deakin doing PAYG deductions, I won't get
    #   the tax back until the end of the financial year, so work out the
    #   amount of tax I will get back into: tax_diff_D_leave
    tax_on_leave = (payout - 45000)*.30 + 4288
    D_leave_after_tax = payout - tax_on_leave

    print(f"my calc would say D_leave_after_tax = {D_leave_after_tax}" )

    # just use quick google/calc - it claims tax rules apply and are capped at 32%  - its possible I get more like 75k then pay tax in July?
    D_leave_after_tax = 59000

    tax_diff_D_leave = payout - D_leave_after_tax

    ### FIXME: for now, assume no tax back after leave - see above comment 
    tax_diff_D_leave = 0

    print( f"tax_diff_D_leave: {tax_diff_D_leave}") 

    ### leave / tax items finished ###

    # convenience vars to make it easier to read conditional leave tax/payment logic below
    D_has_quit = False
    claim_tax_on_leave = False

    # Constants for interest calculations
    annual_interest_rate = Interest_Rate / 100.0
    daily_interest_rate = annual_interest_rate / 365

    # main loop range -- start from now, and simulate till D is 60 (April 2031)
    current_date = datetime.today()


    # refactor Living_Expenses to exclude bills (as we have detailed future projections for them that usually exceed inflation)
    total=0
    yr=str(current_date.year)
    for b in bill_data:
        if yr in b['bill_date']:
            total += b['amount']
    
    print( f"this yr={current_date.year} - total={total}" )
    Living_Expenses -= total
    print( f"LE is now={Living_Expenses}" )

    # Calculate daily living expenses
    daily_living_expenses = Living_Expenses / 365
    print( f"daily LE starts at={daily_living_expenses}" )
    print( f"fortnightly LE starts at={daily_living_expenses*14}" )

    # Start the calculation
    current_savings = Savings
    depletion_date = None
    savings_per_fortnight = []
    
    # significant dates - but who knows when? :)
    overseas_trip_date = datetime.strptime( finance['Overseas_trip_date'], "%Y-%m-%d")
    mark_reno_date = datetime.strptime( finance['Mark_reno_date'], "%Y-%m-%d")
    car_buyout_date = datetime.strptime( finance['Car_buyout_date'], "%Y-%m-%d") 

    # to force deakin pay cycles to match reality, we work from the 8th of Jan as our "day-zero" so we are paid on the 8/1/25, 22/1/25, etc.
    days_count = ( current_date - datetime(2026,1,1) ).days

    # Track the fortnight, and monthly interest
    fortnight_income = 0
    monthly_interest = 0

    # Create an empty dict to store annotations to display in the GUI 
    # (key is date, text is for larger spend items by hand)
    finance['annotations']=[]

    #quick convenience lookup of bill types name for annotations.
    bt_id_name = {row["id"]: row["name"] for row in bill_type}

    while current_date <= end_date:
        #paid on 8th or 22nd of Jan (so 8th day of fortnight)
        is_fortnight = (days_count % 14 == 7)
        is_end_of_month = (current_date.day == 1)
        
        # Subtract daily living expenses
        current_savings -= daily_living_expenses

        # if we have a bill for today, pay for it
        current_savings -= bill_amount_today( finance, current_date, bill_data, bt_id_name, current_savings )

        # Calculate daily interest but apply at the end of the month
        monthly_interest += current_savings * daily_interest_rate

        # Apply fortnightly salary and handle car loan deduction
        if is_fortnight:
            if D_Num_fortnights_pay > 0:
                fortnight_income += D_Salary 
                D_Num_fortnights_pay -= 1

            # keep paying car off fortnightly until the end of the car loan (while I am still working) - once I quit, pay reverts to monthly on the 15th
            if not D_has_quit and current_date < car_balloon_date:
                current_savings -= Car_loan_via_pay
                print( f"{current_date}: making car loan pay as pre-tax lease: ${Car_loan_via_pay}" )

            # no more pay and if leave after tax > 0 this is the day I quit
            if D_Num_fortnights_pay == 0 and D_leave_after_tax > 0:
                D_has_quit = True
                D_quit_date = current_date
                # going to pay tax on payout, so claim it back next year
                claim_tax_on_leave = True
                print(f"{current_date}: D has resigned in new year- get paid out my 12 weeks + remaining leave and lose some to tax - ${D_leave_after_tax}" )
                current_savings += D_leave_after_tax
                add_annotation(finance, current_date, current_savings, D_leave_after_tax, "D quit" )
                D_leave_after_tax = 0

            if fortnight_income:
                print(f"{current_date}: salary paid by Deakin - adding: {fortnight_income}" )
            current_savings += fortnight_income
            fortnight_income = 0  # reset for next fortnight

            savings_per_fortnight.append((current_date.strftime("%Y-%m-%d"), round(current_savings, 2)))

        # its end of fin year, if claim_tax_on_leave > 0 then get tax back 
        if current_date.month == 7 and current_date.day == 1 and claim_tax_on_leave:
            current_savings += tax_diff_D_leave
            print( f"I quit last fin year, so now its 1st July {current_date.year}, get tax back of {tax_diff_D_leave}" )
            add_annotation(finance, current_date, current_savings, tax_diff_D_leave, "D quit - tax back" )
            # can only claim the tax back once :)
            claim_tax_on_leave=False

        # if I have quit, then car lease payments are made on the 15th of the month for full Car_loan
        if D_has_quit and current_date.day == 15:
            if Ioniq6_future == LEASE and current_date <= car_balloon_date:
                current_savings -= Car_loan
                print( f"{current_date}: making car loan pay (after quitting): ${Car_loan}" )
            elif Ioniq6_future != LEASE and current_date <= car_buyout_date:
                current_savings -= Car_loan
                print( f"{current_date}: making car loan pay (after quitting): ${Car_loan}" )

        if is_end_of_month:
            current_savings += monthly_interest
            #print(f"{current_date}: interest paid - ${monthly_interest}")
            monthly_interest = 0

            # monthly increase living expenses by a monthly inflation multiplier
            Living_Expenses += (Inflation/100.0)/12 * Living_Expenses
            daily_living_expenses = Living_Expenses / 365
            print(f"{current_date}: Living Exp inceased - ${Living_Expenses}")

        if current_date.date() == school_fees_date.date():
            current_savings -= School_Fees
            add_annotation(finance, current_date, current_savings, -School_Fees, "School Fees")

        if Ioniq6_future == LEASE and current_date.date() == car_balloon_date.date():
            current_savings -= Car_balloon
            add_annotation(finance, current_date, current_savings, -Car_balloon, "car balloon")
            print(f"{current_date}: car balloon - ${Car_balloon}" )

        if Ioniq6_future != LEASE and current_date.date() == car_buyout_date.date():
            current_savings -= Car_buyout
            add_annotation(finance, current_date, current_savings, -Car_buyout, "car buyout")
            print(f"{current_date}: car buyout - ${Car_buyout}" )

        if current_date.date() == mich_present_date.date():
            current_savings -= Mich_present
            add_annotation(finance, current_date, current_savings, -Mich_present, "Mich's present" )

        if current_date.date() == mark_reno_date.date():
            current_savings -= Mark_reno
            add_annotation(finance, current_date, current_savings, -Mark_reno, "Mark/reno" )

        if current_savings < 0:
            depletion_date = current_date
            break

        # twice a year, CBA has a dividend of $2-2.5 DRP gives back around 20ish shares twice a year, estimate this...
        # and on average they exceed interest rate, but lets assume at least int.rate increase (remember its twice a year, so /2)
        if current_date.day == 1 and (current_date.month == 4 or current_date.month == 10):
            CBA_price = CBA_price+ CBA_price * (Interest_Rate/2)/100
            drp = int( (2.25*D_CBA_shares/CBA_price) )
            print( f"DRP {current_date} - adding {drp} CBA shares" )
            D_CBA_shares += int( (2.25*D_CBA_shares/CBA_price) )

        # if selling shares, and its 1st of July...
        # BUT not if D quits before end of financial year - as I won't be able to sell CBA shares for no cap gains
        # so wait until the following year
        if current_date.month == 7 and current_date.day == 1 and D_has_quit and Sell_shares>0 and (D_quit_date.month<7 or D_quit_date.year < current_date.year ):
            # 2024 Govt. value
            tax_threshold = 18200 
            # cap-gains is 50% of profit (lazy profit calc here, just assume its all profit)
            can_sell = 2*tax_threshold

            actual_sell = 0
            # sell off TLS first - and they are way under the limit, so just sell them all in one hit
            if D_TLS_shares > 0:
                actual_sell += TLS_price*D_TLS_shares
                D_TLS_shares = 0
            if M_TLS_shares > 0:
                actual_sell += TLS_price*M_TLS_shares
                M_TLS_shares = 0
 
            while actual_sell + CBA_price < can_sell and D_CBA_shares > 0:
                actual_sell += CBA_price
                D_CBA_shares -= 1
            Sell_shares -= 1
            
            current_savings += actual_sell
            add_annotation(finance, current_date, current_savings, actual_sell, "Sell shares" )


        current_date += timedelta(days=1)
        days_count += 1

        finance['CBA']=D_CBA_shares
        finance['TLS']=D_TLS_shares+M_TLS_shares

    return depletion_date, savings_per_fortnight, current_savings

################################################################################
# work out the date D quits and when we own the car, so we can then use it to
# handle future bills
################################################################################
def calc_key_dates( finance ):
    key_dates={}
    now = datetime.today()
    # this will be 0 to 13 days - how far into this fortnights pay cycle are we now
    days_in_pay_fortnight= ( now - first_pay_date ).days % 14

    # add 1 less fortnight than we continue to work, then add rest of pay cycle (14-days_in_pay_fortnight)
    key_dates['D_quit_date'] = (now+timedelta(weeks=2*(finance['D_Num_fortnights_pay']-1))+timedelta(days=(14-days_in_pay_fortnight))).strftime('%Y-%m-%d')

    # use lease date
    if finance['Ioniq6_future'] == LEASE:
        key_dates['D_hyundai_owned'] = car_balloon_date.strftime('%Y-%m-%d')
    # use buyout date
    else:
        key_dates['D_hyundai_owned'] = finance['Car_buyout_date']

    return key_dates

################################################################################
# go through finance_history table, find big items to make annoations from
################################################################################
def add_historical_annotations( finance ):

    added={}
    hist=get_historical_data()

    day=hist[0]['M_payout_date'];
    amt=hist[0]['M_payout'];
    dt = datetime.strptime(day, "%Y-%m-%d")

    # cover Mandys resignation
    add_annotation(finance, dt, hist[0]['Savings']+amt, amt, "M Resigned" )

    # cover O/S trip
    for h in hist:
        if 'Overseas_trip' in h and h['Overseas_trip']:
            last_mpd=h['Overseas_trip_date']
            last_mpd_savings=h['Savings']
        snap_dt=datetime.strptime(h['snapshot_date'], "%Y-%m-%d")
        # keep every savings value until we find the last one before the school fees are paid
        if snap_dt < school_fees_date:
            savings_b4_sfd = h['Savings']

    dt = datetime.strptime(last_mpd, "%Y-%m-%d")
    # HARDCODING this, based on post-analysis of CC charges (AND this dt is post most of the spend, so dont adjust)
    amt = 33405.01
    print( f"last_mpd_savings={last_mpd_savings}" )
    add_annotation(finance, dt, last_mpd_savings, -amt, "O/S trip" )
    print( f"should have added o/s trip (-{amt}) - lpd {last_mpd} == {dt}, savings = {last_mpd_savings}" )

    # cover School Fees for 2025
    amt = hist[0]['School_Fees']

    print( f"savings_b4_sfd ={savings_b4_sfd}")
    add_annotation(finance, school_fees_date, savings_b4_sfd-amt, -amt, "Pay school fees" )

    # go through bills from oldest historical date until 'today' and add any > AMT_LIMIT

    return
