# barbacompta - invoicing for dummies
# Copyright (C) 2019-2020 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

from datetime import date, timedelta
from decimal import Decimal

from django import template
from django.db.models import Sum
from django.urls import reverse

from ...decorators import cache
from ..models import LigneBanquePop, solde

register = template.Library()


def month_before(date):
    if date.month == 1:
        return date.replace(year=date.year - 1, month=12)
    else:
        return date.replace(month=date.month - 1)


@register.inclusion_tag("eo_banque/finances.html")
@cache
def finances(month=3):
    dates = [date.today().replace(day=1)]
    for _ in range(1, month):
        dates.append(month_before(dates[-1]))
    e = []
    lignes = LigneBanquePop.objects.filter(date_valeur__gte=min(dates), montant__gt=0)
    for d in dates:
        link = reverse('admin:eo_banque_lignebanquepop_changelist')
        lien_date = link + '?date_valeur__year=%s&date_valeur__month=%s' % (d.year, d.month)
        lien_encaissement = link + '?date_valeur__year=%s&date_valeur__month=%s&montant__gt=0' % (
            d.year,
            d.month,
        )
        #        encaissement = LigneBanquePop.objects \
        #                .filter(date_valeur__year=d.year,
        #                    date_valeur__month=d.month,montant__gt=0) \
        #                .aggregate(Sum('montant'))['montant__sum'] or Decimal('0.00')
        encaissement = Decimal(
            sum(
                ligne.montant
                for ligne in lignes
                if ligne.date_valeur.year == d.year and ligne.date_valeur.month == d.month
            )
        )
        lien_depense = link + '?date_valeur__year=%s&date_valeur__month=%s&montant__lt=0' % (d.year, d.month)

        depense = LigneBanquePop.objects.filter(
            date_valeur__year=d.year, date_valeur__month=d.month, montant__lt=0
        ).aggregate(Sum('montant'))['montant__sum'] or Decimal('0.00')

        e.append(
            {
                'date': d,
                'lien_date': lien_date,
                'encaissement': encaissement,
                'lien_encaissement': lien_encaissement,
                'depense': depense,
                'lien_depense': lien_depense,
                'solde': encaissement + depense,
            }
        )
    return {'finances': e, 'solde': solde()}


@register.inclusion_tag('eo_banque/total.html', takes_context=True)
def total(context):
    qs = context["cl"].get_queryset(context["request"])
    ls = [ligne[0] for ligne in qs.values_list("montant")]
    credit = sum(montant for montant in ls if montant > 0)
    debit = sum(montant for montant in ls if montant < 0)
    total = sum(ls)
    return {'credit': credit, 'debit': debit, 'total': total}


def iso_year_start(iso_year):
    "The gregorian calendar date of the first day of the given ISO year"
    fourth_jan = date(iso_year, 1, 4)
    delta = timedelta(fourth_jan.isoweekday() - 1)
    return fourth_jan - delta


def iso_to_gregorian(iso_year, iso_week, iso_day):
    "Gregorian calendar date for the given ISO year, week and day"
    year_start = iso_year_start(iso_year)
    return year_start + timedelta(iso_day - 1, 0, 0, 0, 0, 0, iso_week - 1)


def days(year, month):
    if month == 12:
        last = date(year + 1, 1, 1) - timedelta(days=1)
    else:
        last = date(year, month + 1, 1) - timedelta(days=1)
    return [date(year, month, i) for i in range(1, last.day + 1)]


def weeks(year, month):
    weeks = [day.isocalendar()[0:2] for day in days(year, month)]
    return sorted(set(weeks))


def week_start_and_end(year, month):
    return [
        (week, iso_to_gregorian(year, week, 1), iso_to_gregorian(year, week, 7))
        for year, week in weeks(year, month)
    ]


@register.inclusion_tag("admin/date_hierarchy.html")
def eo_banque_date_hierarchy(cl):
    if cl.date_hierarchy:
        field_name = cl.date_hierarchy
        year_field = "%s__year" % field_name
        month_field = "%s__month" % field_name
        day_field = "%s__day" % field_name
        field_generic = "%s__" % field_name
        field_gte = "%s__gte" % field_name
        field_lte = "%s__lte" % field_name
        year_lookup = cl.params.get(year_field)
        month_lookup = cl.params.get(month_field)
        day_lookup = cl.params.get(day_field)

        def link(d):
            return cl.get_query_string(d, [field_generic])

        if year_lookup and month_lookup and not day_lookup:
            choices = []
            for week, week_day_start, week_day_end in week_start_and_end(int(year_lookup), int(month_lookup)):
                url = link({field_gte: week_day_start.isoformat(), field_lte: week_day_end.isoformat()})
                choices.append({'title': 'Semaine %s' % week, 'link': url})
            return {'show': True, 'choices': choices}
