# main.py
from flask import Flask, render_template, request, redirect, url_for, Response, jsonify
from calc import calculate_savings_depletion, calc_key_dates, add_historical_annotations
from db import init_db, get_historical_data, get_finance_data, update_finance, get_budget_data
from db import insert_cset, get_comp_set_data, get_comp_set_options, delete_cset
from db import get_bill_freqs, get_bill_growth_types
from db import get_bill_data, new_bill, update_bill_data, delete_bill, delete_estimated_bills, delete_estimated_bills_for
from db import get_bill_ui, save_ui
from db import get_bill_types, insert_bill_type, update_bill_type, delete_bill_type, use_growth
from bills import process_bill_data, calc_future_totals, set_bill_type_growth, recalcFutureBills
from defines import END_YEAR
from collections import defaultdict, Counter
from datetime import datetime, date
import csv
import io
import requests
from disp import FP_VAR

app = Flask(__name__)

# core vars that we will save for comparisons
VARS= ( 'D_Salary', 'D_Num_fortnights_pay', 'School_Fees', 'Car_loan_via_pay', 'Car_loan', 'Car_balloon', 'Living_Expenses', 'Savings', 'Interest_Rate', 'Inflation', 'Mich_present', 'Overseas_trip', 'Mark_reno', 'D_leave_owed_in_days', 'D_TLS_shares', 'M_TLS_shares', 'D_CBA_shares', 'TLS_price', 'CBA_price', 'Overseas_trip_date', 'Mark_reno_date', 'Sell_shares', 'CBA', 'TLS' )

# Initialize the database
init_db()

@app.route('/')
def index():
    hist = get_historical_data()
    finance_data = get_finance_data()
    get_comp_set_options(finance_data)
    bill_data = get_bill_data("order_by_date_only")
    bill_types = get_bill_types()
    depletion_date, savings_per_fortnight, final_savings = calculate_savings_depletion(finance_data, bill_data, bill_types)
    add_historical_annotations( finance_data )
    # now sort this by X, then Y, to get the historical ones in the right order
    finance_data['annotations'].sort(key=lambda item: (item['x'], item['y']))
    BUDGET=get_budget_data(finance_data)

    if depletion_date:
        depletion_date=depletion_date.date(); # just show date

    # if we are comparing...(compare_to will be 0 / None to start with, and then COMP will be None
    if finance_data['compare_to']:
        COMP=get_comp_set_data(finance_data['compare_to'])
    else:
        COMP={}

    DISP=[]
    # Row 1
    r=[]
    r.append( FP_VAR( 'D Salary', 'D_Salary' ) )
    r.append( FP_VAR( 'Savings', 'Savings' ) )
    r.append( FP_VAR( 'Car Loan via Pay', 'Car_loan_via_pay', 'readonly' ) )
    r.append( FP_VAR( 'Living Expenses', 'Living_Expenses' ) )
    r.append( FP_VAR( 'Overseas Trip', 'Overseas_trip', 'date', 'col', 'Overseas_trip_date' ) )
    DISP.append(r)

    # Row 2
    r=[]
    r.append( FP_VAR( 'D # Pays to quit', 'D_Num_fortnights_pay' ) )
    r.append( FP_VAR( 'Interest Rate', 'Interest_Rate' ) )
    r.append( FP_VAR( 'Car Loan (monthly)', 'Car_loan', 'readonly' ) )
    r.append( FP_VAR( 'Inflation', 'Inflation' ) )
    r.append( FP_VAR( 'Reno Costs', 'Mark_reno', 'date', 'col', 'Mark_reno_date' ) )
    DISP.append(r)

    # Row 2
    r=[]
    r.append( FP_VAR( 'D # days leave', 'D_leave_owed_in_days' ) )
    r.append( FP_VAR( 'M TLS amount', 'M_TLS_shares' ) )
    r.append( FP_VAR( 'Car Balloon', 'Car_balloon', 'readonly' ) )
    ss_opt = [ { 'val': 0, 'label': 'Never' } ]
    for el in range(1,7):
        ss_opt.append( { 'val': el, 'label': f'{el} years' } )

    r.append( FP_VAR( 'Sell Shares for:', 'Sell_shares', 'select', 'col-auto', '', ss_opt ) )
    r.append( FP_VAR( 'Car Buyout', 'Car_buyout', 'date', 'col', 'Car_buyout_date' ) )
    DISP.append(r)

    # Row 3
    r=[]
    r.append( FP_VAR( 'D CBA amount', 'D_CBA_shares' ) )
    r.append( FP_VAR( 'D TLS amount', 'D_TLS_shares' ) )
    r.append( FP_VAR( 'Mich Present', 'Mich_present', 'readonly' ) )
    if_opt = [ { 'val': 0, 'label': 'lease' } , {'val': 1, 'label': 'buyout' } ]
    r.append( FP_VAR( 'Ioniq6 future', 'Ioniq6_future', 'select', 'col-auto', '', if_opt ) )
    DISP.append(r)

    # Row 4
    r=[]
    r.append( FP_VAR( 'CBA price', 'CBA_price' ) )
    r.append( FP_VAR( 'TLS price', 'TLS_price' ) )
    r.append( FP_VAR( 'School Fees', 'School_Fees', 'readonly' ) )
    r.append( FP_VAR( 'FINAL # of CBA', 'CBA', 'readonly' ) )
    r.append( FP_VAR( 'FINAL # of TLS', 'TLS', 'readonly' ) )
    DISP.append(r)

    now=datetime.today().strftime('%Y-%m-%d')

    # Extract years from the date strings
    years = [datetime.strptime(date, "%Y-%m-%d").year for date, _ in savings_per_fortnight]

    # Count how many times each year appears
    year_counts = Counter(years)
    # Sort the items by year
    sorted_years = sorted(year_counts.items())  # List of (year, count) tuples

    # Access the first and second years
    first_yr, first_count = sorted_years[0]
    second_yr, second_count = sorted_years[1]

    # now work out how much padding we need in the first year to align the last dates for all years
    padding=second_count - first_count
    key_dates = calc_key_dates( finance_data )
    return render_template('index.html', now=now, first_yr=first_yr, padding=padding, finance=finance_data, depletion_date=depletion_date, savings=savings_per_fortnight, BUDGET=BUDGET, COMP=COMP, DISP=DISP, key_dates=key_dates, hist=hist)

@app.route('/save', methods=['POST'])
def save():
    cset_id=insert_cset( request.get_json() )
    name = request.get_json()['vars']['name']
    return jsonify( cset_id=cset_id, name=name )

@app.route('/update', methods=['POST'])
def update():

    old_finance_data = get_finance_data()

    raw_compare_to = request.form.get('compare_to')
    compare_to_value = None if raw_compare_to == "0" else int(raw_compare_to)

    finance_data = (
        request.form['D_Salary'],
        request.form['D_Num_fortnights_pay'],
        request.form['School_Fees'],
        request.form['Car_loan_via_pay'],
        request.form['Car_loan'],
        request.form['Car_balloon'],
        request.form['Car_buyout'],
        request.form['Living_Expenses'],
        request.form['Savings'],
        request.form['Interest_Rate'],
        request.form['Inflation'],
        request.form['Mich_present'],
        request.form['Overseas_trip'],
        request.form['Mark_reno'],
        request.form['D_leave_owed_in_days'],
        request.form['D_TLS_shares'],
        request.form['M_TLS_shares'],
        request.form['D_CBA_shares'],
        request.form['TLS_price'],
        request.form['CBA_price'],
        request.form['Overseas_trip_date'],
        request.form['Mark_reno_date'],
        request.form['Car_buyout_date'],
        request.form['Sell_shares'],
        compare_to_value,
        request.form['Ioniq6_future']
    )

    update_finance(finance_data)
    new_finance_data = get_finance_data()
    # changed Ioniq6_future, Car_buyout_date or D_Num_fortnights_pay, so lets force recalc key_dates, and therefore estimated bills
    if old_finance_data['D_Num_fortnights_pay'] != new_finance_data['D_Num_fortnights_pay'] or old_finance_data['Ioniq6_future'] != new_finance_data['Ioniq6_future'] or old_finance_data['Car_buyout_date'] != new_finance_data['Car_buyout_date']:
        recalcFutureBills()
    if old_finance_data['Inflation']  != new_finance_data['Inflation']:
        # need to check if any bill type is using CPI, if so, force those future bills to be recalculated
        bill_types = get_bill_types()
        for bt in bill_types:
            if bt['which_growth'] == 'cpi':
                print( f"OK, changed inflation and need to redo bills for bt_id={bt['id']}" )
                delete_estimated_bills_for( bt['id'] )
                #recalc_estimated_bills_for( bt['id'] )
        # okay, now go through code to recalc bills...
        base=request.url_root
        response = requests.get(f"{base}/bills")
        if response.status_code == 200:
            print("ALL GOOD")
        else:
            print("FFS")
    
    return redirect(url_for('index'))

@app.route('/bills')
def DisplayBillData():
    finance_data = get_finance_data()
    # work out when D quits, when car is owned
    key_dates = calc_key_dates( finance_data )
    bill_data = get_bill_data("order_by_bill_type_then_date")
    bill_types = get_bill_types()
    bill_freqs = get_bill_freqs()
    bill_ui = get_bill_ui()
    bill_growth_types = get_bill_growth_types()
    # take bill data, AND work out estimated future bills - process this into the bill_info array, 
    bill_info=process_bill_data(bill_data, bill_types, bill_freqs, key_dates)
    # get an array of the total costs of bills each year - purely cosmetic (using bill_info)
    total=calc_future_totals(bill_info, bill_types)
    # update/re-get bill_data now that new estimated bills have been added
    bill_data = get_bill_data("order_by_bill_type_then_date")
    finance_data = get_finance_data()
    # FIXME: really need to do this better, but for now, hard code til I quit seems okay to me
    FIRST_YEAR=2026
    return render_template('bills.html', bill_data=bill_data, bill_types=bill_types, bill_freqs=bill_freqs, bill_ui=bill_ui, this_year=datetime.today().year, FIRST_YEAR=FIRST_YEAR, END_YEAR=END_YEAR, total=total, key_dates=key_dates, growth=bill_growth_types, cpi=finance_data['Inflation'] )

@app.route('/newbilltype', methods=['POST'])
def InsertBillType():
    data = request.get_json()
    insert_bill_type( data['bill_type'], data['freq'] )
    return "200"

@app.route('/updatebilltype', methods=['POST'])
def UpdateBillType():
    data = request.get_json()
    update_bill_type( data['id'], data['bill_type'], data['freq'] )
    return "200"

@app.route('/newbill', methods=['POST'])
def InsertBill():
    data = request.get_json()
    # last param is estimated - e.g. anything via GUI is not an estimate, but is a real bill
    if 'bill_date' in data:
        new_bill( data['bill_type'], data['amount'], data['bill_date'], 0 )
    else:
        new_bill( data['bill_type'], data['amount'], 'future', 0 )
        set_bill_type_growth( data['bill_type'], 0, 0, 0, data['growth'] )
    return "200"

@app.route('/updatebill', methods=['POST'])
def UpdateBill():
    data = request.get_json()
    update_bill_data( data['id'], data['bill_type'], data['amount'], data['bill_date'] )
    return "200"

@app.route('/delbilltype', methods=['POST'])
def DeleteBillType():
    data = request.get_json()
    delete_bill_type( data['id'] )
    return "200"

@app.route('/delbill', methods=['POST'])
def DeleteBill():
    data = request.get_json()
    delete_bill( data['id'] )
    return "200"

@app.route('/usegrowth', methods=['POST'])
def UseGrowth():
    data = request.get_json()
    use_growth( data['bill_type'], data['which_growth'] )
    return "200"

@app.route('/saveui', methods=['POST'])
def SaveUI():
    data = request.get_json()
    save_ui( data )
    return "200"

@app.route('/force_recalc_bills', methods=['POST'])
def force_recalc_bills():
    delete_estimated_bills()
    recalcFutureBills()
    return "200"

@app.route('/cset')
def cset():
    finance_data = get_finance_data()
    get_comp_set_options(finance_data)
    comp_data={}
    for el in finance_data['COMP_SETS']:
        comp_data[el[0]] = get_comp_set_data( el[0] )
        # delete items not that helpful (same for all, not that interesting)
        if el[0]:
            del comp_data[el[0]]['vars']['Car_loan_via_pay']
            del comp_data[el[0]]['vars']['Mark_reno']
            del comp_data[el[0]]['vars']['Mark_reno_date']
            del comp_data[el[0]]['vars']['Overseas_trip_date']
            del comp_data[el[0]]['vars']['Car_balloon']
            del comp_data[el[0]]['vars']['Mich_present']
            del comp_data[el[0]]['vars']['D_TLS_shares']
            del comp_data[el[0]]['vars']['M_TLS_shares']
        
    return render_template('cset.html', finance=finance_data, comp_data=comp_data )

@app.route('/delcset', methods=['POST'])
def DeleteCSet():
    data = request.get_json()
    delete_cset( data['id'] )
    return "200"

# quick health route so traefik knows we are up
@app.route('/health')
def health():
    return {"status": "ok"}, 200

# Main program
if __name__ == '__main__':
    app.run(debug=True)
