# -*- coding: utf-8 -*-
"""
diacamma.condominium.system package

@author: Laurent GAY
@organization: sd-libre.fr
@contact: info@sd-libre.fr
@copyright: 2018 sd-libre.fr
@license: This file is part of Lucterios.

Lucterios is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Lucterios is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Lucterios.  If not, see <http://www.gnu.org/licenses/>.
"""

from re import match

from django.utils.translation import gettext_lazy as _
from django.db.models.aggregates import Sum

from lucterios.framework.error import LucteriosException, IMPORTANT, GRAVE
from lucterios.framework.tools import get_date_formating
from lucterios.CORE.parameters import Params

from diacamma.accounting.tools import currency_round, current_system_account
from diacamma.accounting.models import FiscalYear, EntryAccount, EntryLineAccount, ChartsAccount, Third, Journal
from diacamma.condominium.models import CallDetail, Owner, PropertyLot, DEFAULT_ACCOUNT_EXCEPTIONNEL


class DefaultSystemCondo(object):

    def __init__(self):
        pass

    def initialize_system(self):
        pass

    def get_config_params(self, _new_params):
        return []

    def get_param_titles(self, names):
        titles = {}
        params = self.get_config_params(False)
        for name in names:
            if name in params:
                titles[name] = _(name)
        return titles

    def check_account_config(self):
        try:
            lettering_check = Params.getvalue("accounting-lettering-check")
            changed = False
            new_lettering = []
            for letter in lettering_check:
                if match(current_system_account().get_third_mask(), letter) is not None:
                    new_lettering.append(letter)
                else:
                    changed = True
            for account in ChartsAccount.objects.filter(year=FiscalYear.get_current(), code__regex=current_system_account().get_societary_mask()):
                if account.code not in new_lettering:
                    new_lettering.append(account.code)
                    changed = True
            if changed:
                Params.setvalue("accounting-lettering-check", ';'.join(new_lettering))
        except LucteriosException:
            pass

    def get_callfunds_list(self, complete=False):
        return []

    def CurrentCallFundsAdding(self, to_create):
        return False

    def owner_account_changed(self, account_item):
        pass

    def _generate_account_callfunds_by_type(self, new_entry, type_call, calldetails):
        raise LucteriosException(IMPORTANT, _('This system condomium is not implemented'))

    def generate_account_callfunds(self, call_funds, fiscal_year):
        for type_call, type_title in self.get_callfunds_list():
            calldetails = call_funds.calldetail_set.filter(type_call=type_call)
            if len(calldetails) > 0:
                designation = _('call of funds "%(type)s" #%(num)d - %(date)s') % {'num': call_funds.num, 'type': type_title,
                                                                                   'date': get_date_formating(call_funds.date)}
                new_entry = EntryAccount.objects.create(year=fiscal_year, date_value=call_funds.date, designation=designation, journal_id=3)
                owner_account_filter = call_funds.supporting.get_third_mask(type_owner=type_call + 1)
                owner_account = call_funds.owner.third.get_account(fiscal_year, owner_account_filter)
                total = self._generate_account_callfunds_by_type(new_entry, type_call, calldetails)
                EntryLineAccount.objects.create(account=owner_account, amount=total, entry=new_entry, third=call_funds.owner.third)

    def generate_revenue_for_expense(self, expense, is_asset, fiscal_year):
        pass

    def generate_expense_for_expense(self, expense, is_asset, fiscal_year):
        third_account = expense.get_third_account(current_system_account().get_provider_mask(), fiscal_year)
        new_entry = EntryAccount.objects.create(year=fiscal_year, date_value=expense.date, designation=expense.__str__(), journal=Journal.objects.get(id=Journal.DEFAULT_BUYING))
        total = 0
        for detail in expense.expensedetail_set.all():
            detail_account = ChartsAccount.get_account(detail.expense_account, fiscal_year)
            if detail_account is None:
                raise LucteriosException(IMPORTANT, _("code account %s unknown!") % detail.expense_account)
            price = currency_round(detail.price)
            EntryLineAccount.objects.create(account=detail_account, amount=is_asset * price, entry=new_entry, costaccounting_id=detail.set.current_cost_accounting.id)
            total += price
        EntryLineAccount.objects.create(account=third_account, amount=is_asset * total, third=expense.third, entry=new_entry)
        no_change, debit_rest, credit_rest = new_entry.serial_control(new_entry.get_serial())
        if not no_change or (abs(debit_rest) > 0.001) or (abs(credit_rest) > 0.001):
            message = _("Error in accounting generator!")
            message += "{[br/]} no_change=%s debit_rest=%.3f credit_rest=%.3f" % (no_change, debit_rest, credit_rest)
            raise LucteriosException(GRAVE, message)
        expense.entries.set(EntryAccount.objects.filter(id=new_entry.id))

    def ventilate_costaccounting(self, fiscal_year, own_set, cost_accounting, type_owner, initial_code):
        if type_owner == DEFAULT_ACCOUNT_EXCEPTIONNEL:
            result = currency_round(CallDetail.objects.filter(set=own_set).aggregate(sum=Sum('price'))['sum'])
        else:
            result = cost_accounting.get_total_revenue()
        result -= cost_accounting.get_total_expense()
        if abs(result) > 0.0001:
            close_entry = EntryAccount(year=fiscal_year, designation=_("Ventilation for %s") % own_set, journal_id=5)
            close_entry.check_date()
            close_entry.save()
            amount = 0
            last_line = None
            for part in own_set.partition_set.all().order_by('value'):
                value = currency_round(result * part.get_ratio() / 100.0)
                if abs(value) > 0.0001:
                    owner_account = part.owner.third.get_account(fiscal_year, part.owner.get_third_mask(type_owner))
                    last_line = EntryLineAccount.objects.create(account=owner_account, amount=-1 * value, entry=close_entry, third=part.owner.third)
                    amount += value
            if last_line is None:
                raise LucteriosException(IMPORTANT, _('The class load %s has no owner') % own_set)
            diff = currency_round(result - amount)
            if abs(diff) > 0.0001:
                last_line.amount -= diff
                last_line.save()
            reserve_account = ChartsAccount.get_account(initial_code, fiscal_year)
            EntryLineAccount.objects.create(account=reserve_account, amount=-1 * result, entry=close_entry, costaccounting=cost_accounting)
            close_entry.closed()

    def ventilate_result(self, fiscal_year, ventilate):
        Owner.throw_not_allowed()
        self.check_account_config()
        result = fiscal_year.total_revenue - fiscal_year.total_expense
        if abs(result) > 0.001:
            total_part = PropertyLot.get_total_part()
            if total_part > 0:
                close_entry = EntryAccount(year=fiscal_year, designation=_("Ventilation for %s") % fiscal_year.toText, journal_id=5)
                close_entry.check_date()
                close_entry.save()
                if ventilate == 0:
                    amount = 0
                    biggerowner_val = 0
                    biggerowner_line = None
                    for owner in Owner.objects.filter(third__status=Third.STATUS_ENABLE):
                        total = owner.propertylot_set.aggregate(sum=Sum('value'))
                        if ('sum' in total.keys()) and (total['sum'] is not None):
                            value = currency_round(result * total['sum'] / total_part)
                            if abs(value) > 0.0001:
                                owner_account = owner.third.get_account(fiscal_year, owner.get_third_mask(1))
                                last_line = EntryLineAccount.objects.create(account=owner_account, amount=-1 * value, entry=close_entry, third=owner.third)
                                if biggerowner_val < total['sum']:
                                    biggerowner_val = total['sum']
                                    biggerowner_line = last_line
                                amount += value
                    diff = currency_round(result - amount)
                    if abs(diff) > 0.0001:
                        biggerowner_line.amount -= diff
                        biggerowner_line.save()
                else:
                    EntryLineAccount.objects.create(account_id=ventilate, amount=result, entry=close_entry)
                reserve_account = ChartsAccount.get_account(Params.getvalue("condominium-current-revenue-account"), fiscal_year)
                EntryLineAccount.objects.create(account=reserve_account, amount=-1 * result, entry=close_entry)
                close_entry.closed()
