# db.py
import sqlite3
import pprint
import os

def connect_db(as_object):
    if 'ENV' in os.environ and os.environ['ENV'] == "production":
        conn = sqlite3.connect('/data/finance.db')
    else:
        conn = sqlite3.connect('./finance.db')
    # allow deleting cset to clear our compare_to properly in finance table
    conn.execute("PRAGMA foreign_keys = ON;")
    if as_object:
        conn.row_factory = sqlite3.Row  # This allows us to access columns by name
    return conn

def init_db():
    conn = connect_db(True)
    cur = conn.cursor()
    cur.execute('''CREATE TABLE IF NOT EXISTS finance (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    D_Salary INTEGER,
                    D_Num_fortnights_pay INTEGER,
                    School_Fees INTEGER,
                    Car_loan_via_pay INTEGER,
                    Car_loan INTEGER,
                    Car_balloon INTEGER,
                    Car_buyout INTEGER,
                    Living_Expenses INTEGER,
                    Savings INTEGER,
                    Interest_Rate REAL,
                    Inflation REAL,
                    Mich_present INTEGER,
                    Overseas_trip INTEGER,
                    Mark_reno INTEGER,
                    D_leave_owed_in_days REAL,
                    D_TLS_shares INTEGER,
                    M_TLS_shares INTEGER,
                    D_CBA_shares INTEGER,
                    TLS_price REAL,
                    CBA_price REAL,
                    Overseas_trip_date STRING,
                    Mark_reno_date STRING,
                    Car_buyout_date STRING,
                    Sell_shares INTEGER,
                    compare_to INTEGER,
                    Ioniq6_future INTEGER
                    )''')
    
    cur.execute('''CREATE TABLE IF NOT EXISTS comparison_set (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name STRING,
                    D_Salary INTEGER,
                    D_Num_fortnights_pay INTEGER,
                    School_Fees INTEGER,
                    Car_loan_via_pay INTEGER,
                    Car_loan INTEGER,
                    Car_balloon INTEGER,
                    Car_buyout INTEGER,
                    Living_Expenses INTEGER,
                    Savings INTEGER,
                    Interest_Rate REAL,
                    Inflation REAL,
                    Mich_present INTEGER,
                    Overseas_trip INTEGER,
                    Mark_reno INTEGER,
                    D_leave_owed_in_days REAL,
                    D_TLS_shares INTEGER,
                    M_TLS_shares INTEGER,
                    D_CBA_shares INTEGER,
                    TLS_price REAL,
                    CBA_price REAL,
                    Overseas_trip_date STRING,
                    Mark_reno_date STRING,
                    Car_buyout_date STRING,
                    Sell_shares INTEGER,
                    CBA INTEGER,
                    TLS INTEGER,
                    Ioniq6_future INTEGER
                    )''')

    cur.execute('''CREATE TABLE IF NOT EXISTS comparison_savings_data (
                    comparison_set_id INTEGER,
                    name STRING,
                    value INTEGER,
                    FOREIGN KEY(comparison_set_id) REFERENCES comparison_set(id)
                    )''')

    cur.execute('''CREATE TABLE IF NOT EXISTS bill_freq (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name STRING,
                    num_bills_per_annum INTEGER
                    )''')

    cur.execute('''CREATE TABLE IF NOT EXISTS bill_type (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    freq INTEGER,
                    name STRING,
                    ann_growth_min REAL,
                    ann_growth_avg REAL,
                    ann_growth_max REAL,
                    ann_growth_simple REAL,
                    FOREIGN KEY(freq) REFERENCES bill_freq(id)
                    )''')

    cur.execute('''CREATE TABLE IF NOT EXISTS bill_data (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    bill_type INTEGER,
                    amount INTEGER,
                    bill_date DATE,
                    estimated INTEGER,
                    FOREIGN KEY(bill_type) REFERENCES bill_type(id)
                    )''')

    cur.execute('''CREATE TABLE IF NOT EXISTS bill_ui (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    last_tab INTEGER,
                    show_estimated INTEGER
                    )''')

    cur.execute('''CREATE TABLE IF NOT EXISTS bill_growth_types (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name string,
                    value INTEGER
                    )''')

    # Check if finance table is empty, if so insert default values
    cur.execute('SELECT COUNT(*) FROM finance')
    if cur.fetchone()[0] == 0:
        cur.execute('''INSERT INTO finance (D_Salary, D_Num_fortnights_pay, School_Fees, Car_loan_via_pay, Car_loan, Car_balloon, Car_buyout, 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, Car_buyout_date, Sell_shares, compare_to, Ioniq6_future)
                        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)''', 
                    (4762.29, 10, 24000, 620, 2412, 45824.68, 83738.74, 80000, 424875.26, 4.75, 2.4, 10000, 50000, 10000, 76.85, 1000, 750, 1111, 4.52, 163.32, '2025-06-01', '2025-09-01', '2025-02-20', 4, 0, 0))

    # Check if bill_freq table is empty, if so insert default values
    cur.execute('SELECT COUNT(*) FROM bill_freq')
    if cur.fetchone()[0] == 0:
        cur.execute( "INSERT INTO bill_freq values ( 1, 'Annual', 1 )" )
        cur.execute( "INSERT INTO bill_freq values ( 2, 'Quarterly', 4 )" )
        cur.execute( "INSERT INTO bill_freq values ( 3, 'Quarterly (forced)', 4 )" )
        cur.execute( "INSERT INTO bill_freq values ( 4, 'Monthly', 12 )" )
        # start with no specific Tab/bill_type to show, and dont show_estimated

    # Check if bill_ui table is empty, if so insert default values
    cur.execute('SELECT COUNT(*) FROM bill_ui')
    if cur.fetchone()[0] == 0:
        cur.execute( "INSERT INTO bill_ui values ( 1, null, 0 )" )

    # Check if bill_growth_types table is empty, if so insert default values
    cur.execute('SELECT COUNT(*) FROM bill_growth_types')
    if cur.fetchone()[0] == 0:
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Min', 0 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Avg', 0 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Max', 0 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Simple', 0 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'CPI', 0 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 0', 0 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 1', 1 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 2', 2 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 3', 3 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 4', 4 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 5', 5 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 6', 6 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 7', 7 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 8', 8 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 9', 9 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 10', 10 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 12', 12 )" )
        cur.execute( "INSERT INTO bill_growth_types ( name, value ) values ( 'Flat 15', 15 )" )

    conn.commit()
    conn.close()

def get_finance_data():
    conn = connect_db(True)
    cur = conn.cursor()
    cur.execute('SELECT * FROM finance WHERE id = 1')  # Assuming a single record for simplicity
    finance = cur.fetchone()
    conn.close()
    return dict(finance)

def get_historical_data():
    conn = connect_db(False)
    # this treats the returns as dicts
    conn.row_factory = sqlite3.Row
    cur = conn.cursor()
    cur.execute('SELECT * FROM finance_history order by snapshot_date')
    hist = [dict(row) for row in cur.fetchall()]
    conn.close()
    return hist

def get_budget_data(finance_data):
    # annual bills - health ins (5k), rates (2.6), electricity (1.2), gas (2.1) - but 1.4 in 2025 due to EU trip, internet (1.6), car insurance (.7), rego (.8), house insurance (2.4), GFC (2.6), water (1.1), eweka (.1), phones (.5), melb. pollen (.03), nabu casa (.1)   --- noting phone is elevated presuming I also go onto Aldi plan, but that there is no family discount, and health will be extra after stop working
    # fudging below - its more like 15.2 + health, and really gas will be more than 2.1 than 1.4, so about 16+5
    bills = 25321.03
    BUDGET=[]
    BUDGET.append( ('Bills', f"${bills:,.2f}") )
    BUDGET.append( ('Buffer', f"${finance_data['CBA']*finance_data['CBA_price']+finance_data['TLS']*finance_data['TLS_price']:,.2f}") )
    BUDGET.append( ('Monthly budget', f"${((finance_data['Living_Expenses']) / 12):,.2f}" ) )
    BUDGET.append( ('Weekly budget', f"${((finance_data['Living_Expenses']) / 52):,.2f}" ) )
    BUDGET.append( ('Monthly budget (ex bills)', f"${((finance_data['Living_Expenses']-bills) / 12):,.2f}" ) )
    BUDGET.append( ('Weekly budget (ex bills)', f"${((finance_data['Living_Expenses']-bills) / 52):,.2f}" ) )
    return BUDGET

def update_finance(data):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute('''UPDATE finance SET
                    D_Salary = ?,
                    D_Num_fortnights_pay = ?,
                    School_Fees = ?,
                    Car_loan_via_pay = ?,
                    Car_loan = ?,
                    Car_balloon = ?,
                    Car_buyout = ?,
                    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 = ?,
                    Car_buyout_date = ?,
                    Sell_shares = ?,
                    compare_to = ?,
                    Ioniq6_future = ?
                    WHERE id = 1''', data)
    conn.commit()
    conn.close()


def insert_cset( data ):
    print( f"Saving: {data['vars']['name']}" )
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute('''INSERT INTO comparison_set (
                    name, D_Salary, D_Num_fortnights_pay, School_Fees, Car_loan_via_pay,
                    Car_loan, Car_balloon, Car_buyout, 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, Car_buyout_date, Sell_shares, CBA, TLS, Ioniq6_future
                    )
                    VALUES (
                    :name, :D_Salary, :D_Num_fortnights_pay, :School_Fees, :Car_loan_via_pay,
                    :Car_loan, :Car_balloon, :Car_buyout, :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, :Car_buyout_date, :Sell_shares, :CBA, :TLS, :Ioniq6_future
                    )
                    ''', data['vars'])
    cset_id = cur.lastrowid

    for d in data['savings_data']:
        cur.execute( f"INSERT INTO comparison_savings_data ( comparison_set_id, name, value  ) VALUES ( {cset_id}, '{d[0]}', '{d[1]}' )" )

    conn.commit()
    conn.close()
    return cset_id

################################################################################
#
# gets comparison set data from the DB based on cset_id passed in (via drop-down in GUI).
# returns a DICT vars pointing to all the var values used for the ARRAY savings_data of saved fortnightly $'s
# also sets date / amount for the last set of $'s for easy display in the GUI
#
################################################################################
def get_comp_set_data(cset_id):
    COMP={}
    # get comp data from DB (as object so dict conversion works below)
    conn = connect_db(True)
    cur = conn.cursor()

    # get saved finance data for this comparison set
    cur.execute( f"select * from comparison_set where id = {cset_id}" )
    res=cur.fetchone()
    if not res:
        return None
    COMP['vars']= dict(res)
    COMP['buffer'] = COMP['vars']['CBA']*COMP['vars']['CBA_price']+COMP['vars']['TLS']*COMP['vars']['TLS_price']
    conn.close()

    # open new connection so we get rows back as basic array
    conn = connect_db(False)
    cur = conn.cursor()

    # get data for this comparison set
    cur.execute( f"select name, value from comparison_savings_data where comparison_set_id = {cset_id}" )
    COMP['savings_data']=cur.fetchall()

    # do this for convenience in printing single last cset data point
    COMP['date'], COMP['amount'] = COMP['savings_data'][-1]
    conn.close()
    return COMP


def get_comp_set_options(finance):
    finance['COMP_SETS']=[ ( 0, 'Nothing' ) ]
    # get comp data from DB (as object so dict conversion works below)
    conn = connect_db(False)
    cur = conn.cursor()

    # get saved finance data for this comparison set
    cur.execute( f"select id, name from comparison_set order by id" )
    finance['COMP_SETS'].extend( cur.fetchall() )
    conn.close()
    return

def get_bill_data(order_by):
    conn = connect_db(True)
    cur = conn.cursor()
    if order_by == "order_by_date_only":
        cur.execute('''SELECT bd.id, bt.id as bill_type, bt.name, bd.amount, bd.bill_date, bd.estimated
            FROM bill_type bt, bill_data bd
            where bt.id = bd.bill_type order by bd.bill_date desc''')
    else:
        cur.execute('''SELECT bd.id, bt.id as bill_type, bt.name, bd.amount, bd.bill_date, bd.estimated
            FROM bill_type bt, bill_data bd
            where bt.id = bd.bill_type order by bt.name, bd.bill_date desc''')
    bd = cur.fetchall()
    conn.close()
    return bd

def get_bill_types():
    conn = connect_db(True)
    cur = conn.cursor()
    cur.execute('SELECT * FROM bill_type order by name')
    bt = cur.fetchall()
    conn.close()
    return bt

def use_growth( bill_type, which_growth ):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"update bill_type set which_growth = '{which_growth}' where id = {bill_type}" )
    # okay, new growth type being used, delete old estimated bills are recreate them
    cur.execute( f"delete from bill_data where estimated=1 and bill_type = {bill_type}" )
    conn.commit()
    conn.close()
    return

def get_bill_freqs():
    conn = connect_db(True)
    cur = conn.cursor()
    cur.execute('SELECT * FROM bill_freq order by name')
    bf = cur.fetchall()
    conn.close()
    return bf


def new_bill( bill_type, amount, bill_date, estimated ):
    conn = connect_db(False)
    cur = conn.cursor()
    # if we are a real bill added by UI
    if not estimated:
        # delete old estimates as new bill will potentially change them/growth, etc.
        cur.execute( f"delete from bill_data where estimated=1" )
        # force the next /bills load to show the tab for the bill we are adding
        cur.execute( f"update bill_ui set last_tab='{bill_type}'" )
    cur.execute( f"insert into bill_data ( 'bill_type', 'amount', 'bill_date', 'estimated' ) values ( '{bill_type}', '{float(amount):.2f}', '{bill_date}', {estimated} )" )
    conn.commit()
    conn.close()
    return

def update_bill_data( id, name, amount, bill_date ):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"update bill_data set bill_type =(select id from bill_type where name ='{name}'), amount='{amount}', bill_date='{bill_date}' where id = {id}" )
    conn.commit()
    conn.close()
    return

def insert_bill_type( bt, fq ):
    conn = connect_db(False)
    cur = conn.cursor()
    print( f"fq={fq}" )
    cur.execute( f"insert into bill_type ( 'name', 'freq', 'ann_growth_min', 'ann_growth_avg', 'ann_growth_max', 'ann_growth_simple' ) values ( '{bt}', {fq}, 0, 0, 0, 0 )" )
    conn.commit()
    conn.close()
    return

def update_bill_type(id, bill_type, freq):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"update bill_type set name ='{bill_type}', freq={freq} where id = {id}" )
    conn.commit()
    conn.close()
    return

def delete_bill(id):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"delete from bill_data where id = '{id}'" )
    conn.commit()
    conn.close()
    return

def delete_bill_type( id ):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"delete from bill_type where id = '{id}'" )
    conn.commit()
    conn.close()
    return

def set_bill_type_growth( id, min_g, avg_g, max_g, simple_g ):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"update bill_type set ann_growth_min={min_g}, ann_growth_avg ={avg_g}, ann_growth_max={max_g}, ann_growth_simple= {simple_g} where id = {id}" )
    conn.commit()
    conn.close()
    return

def get_bill_growth_types():
    conn = connect_db(True)
    cur = conn.cursor()
    cur.execute('SELECT * FROM bill_growth_types')
    growth = cur.fetchall()
    conn.close()
    return growth

def get_bill_ui():
    conn = connect_db(True)
    cur = conn.cursor()
    # only ever be 1
    cur.execute('SELECT * FROM bill_ui')
    ui = cur.fetchone()
    conn.close()
    return ui

def save_ui(data):
    conn = connect_db(False)
    cur = conn.cursor()
    if 'last_tab' in data:
        cur.execute( f"update bill_ui set last_tab='{data['last_tab']}'" )
    if 'show_estimated' in data:
        cur.execute( f"update bill_ui set show_estimated='{data['show_estimated']}'" )
    conn.commit()
    conn.close()
    return


def deleteFutureEstimates():
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( "delete from bill_data where bill_date != 'future' and  bill_type in ( select bill_type from bill_data where  bill_date='future')" )
    conn.commit()
    conn.close()
    return

def delete_estimated_bills():
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( "delete from bill_data where estimated=1" )
    conn.commit()
    conn.close()
    return

def delete_estimated_bills_for(bt_id):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"delete from bill_data where estimated=1 and bill_type = {bt_id}" )
    conn.commit()
    conn.close()
    return

def delete_cset(id):
    conn = connect_db(False)
    cur = conn.cursor()
    cur.execute( f"delete from comparison_set where id = '{id}'" )
    conn.commit()
    conn.close()
    return
