from .DBConnection import DBConnection
import os
from sankeyflow import Sankey
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
import datetime
import numpy as np
from colorsys import hls_to_rgb
import re



class BudgetLine():
    def __init__(self, desc:str, charge:int, revenue:int):
        self.desc = desc
        self.charge = charge
        self.revenue = revenue

    
class Account():
    def __init__(self, label:str, number:int, tmp:int):
        self.label = label
        self.number = number
        self.type = tmp


class FinanceDBConnection(DBConnection):
    def get_sum_of_operations(self, date:str, account:Account):
        VERBOSE = False
        tot_cred = tot_deb = 0
        self.conn.execute(
            "SELECT piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit FROM llx_accounting_bookkeeping WHERE numero_compte = ? AND doc_date >= ?",
            (account.number, date)
        )
        last_entrie = date
        for piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit in self.conn : 
            tot_cred += credit
            tot_deb += debit
            if str(doc_date) > last_entrie : 
                last_entrie = str(doc_date)
        
        if VERBOSE : print(f'pour le compte {account.number} : credit = {tot_cred} / debit = {tot_deb} à partir du {date}')
        return int(tot_deb), int(tot_cred), last_entrie

    def get_income_and_expense_accounts(self):
        VERBOSE = False
        if VERBOSE : print("get_income_and_expense_accounts")
        self.conn.execute(
            "SELECT account_number, label  FROM llx_accounting_account WHERE fk_pcg_version = 'PCG-BSC' AND active = 1;"
        )
        accounts = []
        for account_number, label in self.conn:
            tmp = account_number[0] 
            if (tmp == '6' or tmp == '7') and int(account_number) > 10:
                accounts.append(Account(label, account_number, int(tmp)))
        
        if VERBOSE : [print(f'account {a.number} / {a.label}') for a in accounts]
        return accounts


    def get_bank_reserves(self, date:str):
        VERBOSE = False
        if VERBOSE : print('get_bank_reserves')
        total = 0
        self.conn.execute(
            "SELECT piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit FROM llx_accounting_bookkeeping WHERE (numero_compte = ? OR numero_compte = ?) AND doc_date = ? AND code_journal = ?;",
            (5121, 5311, date, 'AN',)
        )
        for piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit in self.conn:
            #print(piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit, sep=" | ")
            if VERBOSE : print(f'ajout de : {debit} provenant du compte {label_compte} au total')
            total += debit
        if VERBOSE : print(f'total = {total}')
        return total



class FinanceTable():
    def __init__(self):
        self.ft:list[BudgetLine]

    def sort_des_table(self, li:int):
        if li < len(self.ft)-1 :
            ch = self.ft[li].charge
            rev = self.ft[li].revenue
            if (ch > 0 and ch >= self.ft[li+1].charge) or (ch == 0 and self.ft[li+1].charge ==0 and rev >=self.ft[li+1].revenue ):
                self.sort_des_table(li+1)
            else : 
                if li == 0 : 
                    self.ft = [self.ft[li+1], self.ft[li]] + self.ft[li+2:]
                    self.sort_des_table( li)
                elif li < len(self.ft) - 2 : 
                    self.ft = self.ft[:li] + [self.ft[li+1], self.ft[li]] + self.ft[li+2:]
                    self.sort_des_table( li-1)
                else : 
                    self.ft = self.ft[:li] + [self.ft[li+1], self.ft[li]]
                    self.sort_des_table(li-1)
    

    def get_revenue_ratios(self, tot:int, budget:list[BudgetLine]):
        values = {}
        oth = 0
        coef = 4
        i = 0
        for line in budget:
            if line.revenue >0 :
                if i < coef:
                    values.update(
                        {line.desc:line.revenue}
                    )
                    i+=1
                else: 
                    oth += line.revenue
        if oth > 0 :
            values.update(
                {'Autres':oth}
            )
        return values


    def get_charge_ratios(self, tot:int, budget:list[BudgetLine]):
        values = {}
        oth = 0
        coef = 4
        i = 0
        for line in budget:
            if line.charge >0 :
                if i < coef:
                    values.update(
                        {line.desc:line.charge}
                    )
                    i+=1
                else: 
                    oth += line.charge
        if oth > 0 :
            values.update(
                {'Autres':oth}
            )
        return values
    

    def get_total_charges(self, budget:list[BudgetLine]):
        tot = 0
        for line in budget:
            tot += line.charge
        return int(tot)


    def get_total_revenues(self, budget:list[BudgetLine]):
        tot = 0
        for line in budget:
            tot += line.revenue
        return int(tot)


                

class Budget(FinanceTable):
    def __init__(self, csv:str):
        with open(csv, "r") as file:
            content = file.read()     
        self.ft = []
        for line in content.split('\n'):
            tmp = line.split(';')
            self.ft.append(
                BudgetLine(
                    desc=tmp[0],
                    charge=int(tmp[1]),
                    revenue=int(tmp[2])
                )
        )

class Finance(FinanceTable):
    def __init__(self, cursor):
        # extraire le compte et la caisse au 1 er janvier de l'année
        year = datetime.datetime.now().year
        first_day = rf'{year}-01-01'
        accounts = get_income_and_expense_accounts(cursor)
        self.table = []
        self.last_update = first_day
        for a in accounts:
            tot_deb, tot_cred, last_entrie = get_sum_of_operations(
                                    cursor,
                                    first_day,
                                    a
                                )
            if a.type == 6 :
                #charge
                self.table.append(BudgetLine(a.label, tot_deb - tot_cred, 0))
            elif a.type == 7:
                self.table.append(BudgetLine(a.label, 0, tot_cred - tot_deb))
            if last_entrie >= self.last_update : 
                self.last_update = last_entrie






def extract_value(content:str, param:str):
    tmp = re.search(rf'define...{param}.+', content)
    if tmp is not None: 
        return tmp.group().split(',')[1].split('\'')[1]
    else:
        return ""




def get_budget_elements(budget:list[BudgetLine]):
    nodes = []
    flows=[]
    total = 0
    CL = []
    RL = []
    for line in budget:
        charge = line.charge
        desc = line.desc
        revenue = line.revenue
        if charge != 0 and revenue == 0:
            total += charge
            CL.append((desc, charge, {'label_pos':'right', 'color':'#007e97' }))
            flows.append(('Total', desc, charge))

        elif charge == 0 and revenue != 0:
            RL.append((desc, revenue, {'label_pos':'left', 'color':'#007e97' }))
            flows.append((desc, 'Total', revenue))

    nodes = [
        RL,
        [('Total', total, {'label_pos':'top', 'color':'#007e97' })],
        CL
    ]

    return nodes, flows


def create_budget_sankey(
        budget:list[BudgetLine],
        save:str):
    
    nodes, flows = get_budget_elements(budget)
    plt.figure(figsize=(25, 10), dpi=144)
    s = Sankey(
        flows=flows,
        nodes=nodes,
        node_opts=dict(label_format='{label}:{value}')
    )
    s.draw()
    path = os.path.join(save, 'budget.svg')
    plt.savefig(path)
    plt.close()

def get_totals_lists_for_charts(tot_ch_bud, tot_ch_real, tot_re_bud, tot_re_real):
    rest_ch = abs(tot_ch_bud - tot_ch_real)
    rest_re = abs(tot_re_bud - tot_re_real)
    if tot_ch_bud >= tot_ch_real  and tot_re_bud >= tot_re_real:
        
        values = {
            f'actuel' : [tot_re_real, tot_ch_real],
            f'budget' : [rest_re, rest_ch]
        }
    else : 
        values = {
            f'bot_budget' : [int(0.98 * tot_re_bud), int(0.98 * tot_ch_bud)],
            f'budget' :  [int(0.02 *tot_re_bud), int(0.02 *tot_ch_bud)],
            f'actuel' : [rest_re, rest_ch]
        }
    return values


def hls_to_rgbs(s_hsl): 
	rgb = hls_to_rgb(s_hsl["Hue"], s_hsl["Luminosité"], s_hsl["Saturation"] )
	return "#" + "".join("%02X" % round(i*255) for i in rgb )


def get_color_list(nb, s_hsl, sens = "Blanc") :
    temp = s_hsl
    luminosite = temp["Luminosité"]
    
    if sens == "Blanc":
        ecart = (1-luminosite ) /(nb +1)
    else : 
        ecart = luminosite / (nb + 1)
	
    l_couleur = []
    for x in range(nb) :
        l_couleur.append(hls_to_rgbs(temp ))
        if sens == "Blanc" :
            luminosite += ecart	
        else :
            print(luminosite)
            luminosite -= ecart	
        temp["Luminosité"] = luminosite
    return l_couleur


def create_pie_diagrams(path:str,
                    revenue_ratios:dict[str, int],
                    charge_ratios:dict[str, int],
                    last_update:str):
    
    bleu_hls = {"Hue":190 / 360, 
                "Saturation":100/100,
                "Luminosité":30/100}
    
    marron_hls = {"Hue":23 / 360, 
                "Saturation":72/100,
                "Luminosité":30/100}

    fig, axs = plt.subplots(ncols=2,figsize=(10, 4), layout='constrained')
    labels, values = transform_dict_for_diag(revenue_ratios)
    colors = get_color_list(len(values),bleu_hls)
    p = axs[0].pie(x=values, labels=labels, colors=colors)
    
    labels, values = transform_dict_for_diag(charge_ratios)
    colors = get_color_list(len(values),marron_hls)
    p = axs[1].pie(x=values, labels=labels, colors=colors)
    
    axs[1].set_title('Répartition des dépenses réalisées')
    axs[0].set_title('Répartition des recettes réalisées')
    path = os.path.join(path, 'ratios.svg')
    plt.savefig(path)
    plt.close()


def transform_dict_for_diag(dicts:dict):
    labels = []
    values = []
    for label, value in dicts.items():
        labels.append(f'{label}\n{value}€')
        values.append(value)

    return labels, values


def create_total_diagram(path:str,
                    totals: dict[str, list[int]],
                    last_upadate:str):
    
    fig, ax = plt.subplots()
    plt.grid(True, 'major', 'y', color="#555555")
    width = 0.6
    types = ('dépenses', 'revenus')
    leg = [mpatches.Patch(color='#007e97', label=f'au {last_upadate}'),
               mpatches.Patch(color='#999999', label='budgeté')
            ]
    bottom = np.zeros(2)
    if len(totals) ==2 : 
        col = ['#007e97', '#999999']
    else:
        col = ['#007e97', '#999999', '#007e97']
        
    x = 0
    for position, values in totals.items():
        p = ax.bar(types, values, width, label=position, bottom=bottom, color=col[x] )
        bottom += values
        x+=1

    #ax.invert_yaxis()  # labels read top-to-bottom
    
    ax.set_title('Total des dépenses et des revenus de 2025')
    box = ax.get_position()
    ax.set_position(
        [box.x0, box.y0 + box.height * 0.1,
         box.width, box.height * 0.9]# type: ignore
         ) 
    ax.legend(handles=leg, loc='upper center', bbox_to_anchor=(0.5, -0.07),
          fancybox=True, shadow=True, ncol=2)
    path = os.path.join(path, 'total.svg')
    plt.savefig(path)
    plt.close()


def create_accounting_sankey(cursor, save_dir):
    NR = []
    NC = []
    flows = []

    # extraire le compte et la caisse au 1 er janvier de l'année
    year = datetime.datetime.now().year
    first_day = rf'{year}-01-01'
    reserves = int(get_bank_reserves(cursor, first_day))
    ro_name = 'reserves au début de l\'année'
    ra_name = 'reserves aujourd\'hui'
    t_name = 'Total'
    NR.append((ro_name, reserves, {'label_pos':'left', 'color':'#007e97'}))
    flows.append((ro_name, t_name, reserves))

    # lister les comptes de charges et produit
    accounts = get_income_and_expense_accounts(cursor)
    tmp_res = reserves
    tot = reserves
    for a in accounts:
        tot_deb, tot_cred, last_entrie = get_sum_of_operations(
                                cursor,
                                first_day,
                                a
                            )
        tmp_res = tmp_res + tot_cred - tot_deb
        if a.type == 6 :
            tmp_tot = tot_deb-tot_cred
            if tmp_tot > 0 :
                NC.append((a.label, tmp_tot, {'label_pos':'right', 'color':'#007e97' }))
                flows.append((t_name, a.label, tmp_tot))
        else :
            tmp_tot = tot_cred-tot_deb
            if tmp_tot > 0 :
                tot += tmp_tot
                NR.append((a.label, tmp_tot, {'label_pos':'left', 'color':'#007e97' }))
                flows.append((a.label, t_name, tmp_tot))

    NC.insert(0, (ra_name, tmp_res, {'label_pos':'right', 'color':'#007e97'}))
    flows.insert(0, (t_name, ra_name, tmp_res))
    nodes = [
        NR,
        [('Total', tot, {'label_pos':'top', 'color':'#007e97'})],
        NC
    ]

    plt.figure(figsize=(30, 10), dpi=144)
    s = Sankey(
        flows=flows,
        nodes=nodes,
        node_opts=dict(label_format='{label}:{value}')
    )
    s.draw()
    path = os.path.join(save_dir, 'compta.svg')
    plt.savefig(path)
    plt.close()

