import datetime
import logging
import math

from median.constant import TypeServiListe
from median.models import ListeValide, ItemValide, Magasin, Seuil
from peewee import fn
from ressources.acced.production.acced_production_service import State, _get_config, CONTRAT_COMPLETION_PERCENTAGE
from ressources.acced.production.acced_production_service import get_equipment_case

logger = logging.getLogger('median.webserver')


def get_completion_mean(start, end, type_mag):
    completions = get_completion_data(start=start, end=end, type_mag=type_mag)

    completed = sum(map(lambda r: r['prescription_qty'] - r['served_qty'], completions))
    total = sum(map(lambda r: r['prescription_qty'], completions))

    mean = round((1 - (completed / total)) * 100, 2) if total > 0 else None
    state = State.UNKNOW.value if mean is None or math.isnan(mean) else State.KO.value
    threshold = _get_config(property=CONTRAT_COMPLETION_PERCENTAGE)

    if state != State.UNKNOW.value:
        if mean >= threshold:
            state = State.OK.value
        elif mean >= (0.9 * threshold):
            state = State.WARNING.value

    return {
        'mean': mean,
        'state': state,
        'threshold': {
            'min': threshold,
            'warning': round(0.9 * threshold),
            'unit': 'unit.percentage'
        }
    } if state != State.UNKNOW.value else None


def get_completion_data(start, end, type_mag):

    equipment_case = get_equipment_case()
    end_date = datetime.datetime.strptime(end.replace('/', '-'), "%Y-%m-%d") + datetime.timedelta(days=1)
    threshold_subquery = list(Seuil.select(Seuil.fraction, Seuil.reference).where(
        (Seuil.zone == type_mag)))

    completions = ((ListeValide.select(fn.YEAR(ListeValide.chrono).alias('year'),
                                       fn.WEEKOFYEAR(ListeValide.chrono).alias('week_num'),
                                       fn.SUM(ItemValide.quantite_dem).alias('prescription_qty'),
                                       fn.SUM(ItemValide.quantite_serv).alias('served_qty'),
                                       ItemValide.reference.alias('reference'),
                                       ItemValide.fraction.alias('fraction'))
                               .join(ItemValide, on=ItemValide.liste_pk == ListeValide.pk)
                               .join(Magasin, on=Magasin.mag == equipment_case)
                               .where((ListeValide.chrono >= start) &
                                      (ListeValide.chrono <= end_date) &
                                      (Magasin.type_mag == type_mag) &
                                      (ListeValide.type_servi == TypeServiListe.Nominatif.value)
                                      )
                               .order_by(ListeValide.chrono)
                               .group_by(fn.WEEKOFYEAR(ListeValide.chrono), ItemValide.reference, ItemValide.fraction))
                   .objects()) if equipment_case is not None else []

    res = []

    for completion in completions:

        week = next(filter(lambda c: c['date']['num'] == completion.week_num and
                           c['date']['year'] == completion.year, res), None)

        if week is None:
            week = {
                'date': {'num': completion.week_num, 'year': completion.year},
                'prescription_qty': 0,
                'served_qty': 0
            }
            res.append(week)

        if next(filter(lambda s: s.reference == completion.reference and
                       s.fraction == completion.fraction, threshold_subquery), None) is not None:

            if completion.served_qty > completion.prescription_qty:
                completion.served_qty = completion.prescription_qty

            week['prescription_qty'] = week['prescription_qty'] + completion.prescription_qty
            week['served_qty'] = week['served_qty'] + completion.served_qty

    return res
