import re

from . import *


class TBLine:
    def __init__(self, tb_line: list):
        self.balance_type = tb_line[0]
        self.entity_code = tb_line[1]
        self.entity_name = tb_line[2]
        self.month = tb_line[3]
        self.year = tb_line[4]
        self.account_code = tb_line[5]
        self.account_name = tb_line[6]
        self.currency_code = tb_line[7]
        self.currency_name = tb_line[8]
        self.begin_balance_ori_currency = tb_line[9]
        self.year_debit_ori_currency = tb_line[10]
        self.year_credit_ori_currency = tb_line[11]
        self.period_debit_ori_currency = tb_line[12]
        self.period_credit_ori_currency = tb_line[13]
        self.end_balance_ori_currency = tb_line[14]
        self.begin_balance_local_currency = tb_line[15]
        self.year_debit_local_currency = tb_line[16]
        self.year_credit_local_currency = tb_line[17]
        self.period_debit_local_currency = tb_line[18]
        self.period_credit_local_currency = tb_line[19]
        self.end_balance_local_currency = tb_line[20]
        self.begin_balance_report_currency = tb_line[21]
        self.year_debit_report_currency = tb_line[22]
        self.year_credit_report_currency = tb_line[23]
        self.period_debit_report_currency = tb_line[24]
        self.period_credit_report_currency = tb_line[25]
        self.end_balance_report_currency = tb_line[26]
        self.year_pl_ori_currency = tb_line[27]
        self.period_pl_ori_currency = tb_line[28]
        self.year_pl_local_currency = tb_line[29]
        self.period_pl_local_currency = tb_line[30]
        self.year_pl_report_currency = tb_line[31]
        self.period_pl_report_currency = tb_line[32]
        self.begin_qty = tb_line[33]
        self.year_debit_qty = tb_line[34]
        self.year_credit_qty = tb_line[35]
        self.period_debit_qty = tb_line[36]
        self.period_credit_qty = tb_line[37]
        self.end_qty = tb_line[38]
        self.asst_code = tb_line[39]
        self.asst_name = tb_line[40]
        self.has_asst = tb_line[41]
        self.has_asst_bool = True if tb_line[41] == '1' else False


class AsstTBLine:
    def __init__(self, tb_line: list):
        self.balance_type = tb_line[0]
        self.entity_code = tb_line[1]
        self.entity_name = tb_line[2]
        self.month = tb_line[3]
        self.year = tb_line[4]
        self.account_code = tb_line[5]
        self.account_name = tb_line[6]
        self.currency_code = tb_line[7]
        self.currency_name = tb_line[8]
        self.begin_balance_ori_currency = tb_line[9]
        self.year_debit_ori_currency = tb_line[10]
        self.year_credit_ori_currency = tb_line[11]
        self.period_debit_ori_currency = tb_line[12]
        self.period_credit_ori_currency = tb_line[13]
        self.end_balance_ori_currency = tb_line[14]
        self.begin_balance_local_currency = tb_line[15]
        self.year_debit_local_currency = tb_line[16]
        self.year_credit_local_currency = tb_line[17]
        self.period_debit_local_currency = tb_line[18]
        self.period_credit_local_currency = tb_line[19]
        self.end_balance_local_currency = tb_line[20]
        self.begin_balance_report_currency = tb_line[21]
        self.year_debit_report_currency = tb_line[22]
        self.year_credit_report_currency = tb_line[23]
        self.period_debit_report_currency = tb_line[24]
        self.period_credit_report_currency = tb_line[25]
        self.end_balance_report_currency = tb_line[26]
        self.year_pl_ori_currency = tb_line[27]
        self.period_pl_ori_currency = tb_line[28]
        self.year_pl_local_currency = tb_line[29]
        self.period_pl_local_currency = tb_line[30]
        self.year_pl_report_currency = tb_line[31]
        self.period_pl_report_currency = tb_line[32]
        self.begin_qty = tb_line[33]
        self.year_debit_qty = tb_line[34]
        self.year_credit_qty = tb_line[35]
        self.period_debit_qty = tb_line[36]
        self.period_credit_qty = tb_line[37]
        self.end_qty = tb_line[38]
        self.asst_code = tb_line[39]
        self.asst_name = tb_line[40]
        self.asst_full_desc = tb_line[41]
        self.asst_full_name = tb_line[42]
        self.asst_full_code = tb_line[43]
        # parse asst information
        asst_type_list = self.asst_full_code.split(';')
        self.asst_type1 = asst_type_list[0].split('_!')[1].split(':')[0]
        self.asst_code1 = asst_type_list[0].split('_!')[1].split(':')[1]
        try:
            self.asst_type2 = asst_type_list[1].split('_!')[1].split(':')[0]
            self.asst_code2 = asst_type_list[1].split('_!')[1].split(':')[1]
        except IndexError:
            self.asst_type2 = None
            self.asst_code2 = None

    def get_main_info_str(self):
        info = ' '.join(['balance_type', self.balance_type,
                         'currency_code', self.currency_code,
                         'account_code', self.account_code,
                         'asst_type', self.asst_type1,
                         'asst_code', self.asst_code1,
                         'year_end_balance', self.end_balance_local_currency])
        return info


class AsstTB:
    def __init__(self, tb: list):
        self.asst_list = []
        if tb:
            for each in tb:
                if not re.match('^\\d', str(each[0])):
                    continue
                self.asst_list.append(AsstTBLine(each))


class TB:
    def __init__(self):
        """
        Tested the size of one year end tb, with all asst accounts info, the size was approximately 24.4M
        which should be considered acceptable.
        However, user should be very careful about loading more than one month's trial balance, and all its
        asst ledger, the object may be very large, since sometimes user may want to look up information over several years,
        especially used in web server.
        :param tb: the original result from kd webservice
        """
        self.record_list = []
        self.asst_tb_dict = {}

    def add_tb(self, tb: list):
        if tb:
            for line in tb:
                if not re.match('^\\d', str(line[0])):
                    continue
                self.record_list.append(TBLine(line))

    def is_leaf_account(self, account_code: str) -> bool:
        for each in self.record_list:
            if re.match(account_code + '.+', each.account_code):
                return False
        return True

    def add_asst_tb(self, asst_tb_obj: [AsstTB, list]):
        if type(asst_tb_obj) == AsstTB:
            if not asst_tb_obj.asst_list:
                return -1
            self.asst_tb_dict[asst_tb_obj.asst_list[0].account_code] = asst_tb_obj
            return 0
        elif type(asst_tb_obj) == list:
            if not asst_tb_obj:
                return -1
            try:
                o = AsstTB(asst_tb_obj)
                return self.add_asst_tb(o)
            except Exception:
                raise ObjInitException


def separate_voucher_list(voucher_list: list, renumber=False) -> dict:
    """
    clean the voucher list
    :param voucher_list: use voucher number and company number as unique key to identify voucher,
     the key to result dictionary is 'voucherNumber_companyNumber'
    :param renumber: WARNING! if this argument is True,
     the function will comb the list and renumber the sequence of entry, which may lead unknown error.
     Especially in finance booking term. Developer who has NO finance background, please use this argument carefully.
    :return: the dict of voucher list, the key should be the voucher number
    """
    seperated_voucher_dict = {}
    for voucher in voucher_list:
        voucher_key = '_'.join([str(voucher['voucherNumber']), str(voucher['companyNumber'])])
        if voucher_key not in seperated_voucher_dict:
            seperated_voucher_dict.setdefault(voucher_key, [])
        seperated_voucher_dict[voucher_key].append(voucher)
    if renumber:
        for voucher_key in seperated_voucher_dict:
            for new_entry_int, voucher in enumerate(seperated_voucher_dict[voucher_key]):
                voucher['entrySeq'] = new_entry_int + 1
    return seperated_voucher_dict


def _get_standard_voucher(assit=0):
    """
    this function is in order to help the developer remember the field name of voucher object.
    :param assit:
    :return:
    """
    standard_voucher = {
        'companyNumber': '',
        'bookedDate': '',
        'bizDate': '',
        'periodYear': 0,
        'periodNumber': 0,
        'voucherType': '',
        'voucherNumber': '',
        'entrySeq': 0,
        'voucherAbstract': '',
        'accountNumber': '',
        'currencyNumber': '',
        'localRate': 1,
        'entryDC': 0,
        'originalAmount': 0.0,
        'debitAmount': 0.0,
        'creditAmount': 0.0,
        'creator': '',
    }
    if assit == 1:
        standard_voucher['asstActType1'] = ''
        standard_voucher['asstActNumber1'] = ''
    elif assit == 2:
        standard_voucher['asstActType1'] = ''
        standard_voucher['asstActNumber1'] = ''
        standard_voucher['asstActType2'] = ''
        standard_voucher['asstActNumber2'] = ''
    return standard_voucher
