import math

from median.models import HistoriqueBlocage, Magasin, SuiviProd, CodeBlocage
from peewee import fn, JOIN

from ressources.acced.production.acced_production_service import _get_config, CONTRAT_BLOCKED_BOX_PER_HOUR, State


def get_box_blocked_data(start, end, type_mag, counter_values):
    blocked_boxes = (HistoriqueBlocage.select(fn.YEAR(HistoriqueBlocage.chrono).alias('year'),
                                              fn.WEEKOFYEAR(HistoriqueBlocage.chrono).alias('num_week'),
                                              HistoriqueBlocage.bloque.alias('code'),
                                              CodeBlocage.libelle.alias('label'),
                                              fn.COUNT(HistoriqueBlocage.pk).alias('blocked_num'))
                                      .join(Magasin, on=Magasin.mag == fn.SUBSTRING(HistoriqueBlocage.adresse, 1, 3))
                                      .join(CodeBlocage, JOIN.LEFT_OUTER,
                                            on=CodeBlocage.valeur == HistoriqueBlocage.bloque)
                                      .where((HistoriqueBlocage.chrono >= start) &
                                             (HistoriqueBlocage.chrono <= end) &
                                             (HistoriqueBlocage.adresse.is_null(False)) &
                                             (Magasin.type_mag == type_mag) &
                                             (HistoriqueBlocage.bloque != 0))
                                      .group_by(fn.YEAR(HistoriqueBlocage.chrono).alias('year'),
                                                fn.WEEKOFYEAR(SuiviProd.chrono).alias('num_week'),
                                                HistoriqueBlocage.bloque)
                     .order_by(HistoriqueBlocage.chrono.asc())
                     )

    blocked_boxes_ratio = []

    for blocked in blocked_boxes.objects():
        elt = next(filter(lambda v: v['date']['num'] == blocked.num_week and
                          v['date']['year'] == blocked.year, blocked_boxes_ratio), None)
        pick_time = next(filter(lambda v: v['date']['num'] == blocked.num_week and
                                v['date']['year'] == blocked.year, counter_values), None)

        if elt is None:
            elt = {
                'date': {'num': blocked.num_week, 'year': blocked.year},
                'ratio': 0,
                'reason': [],
                'blocked': 0
            }
            blocked_boxes_ratio.append(elt)

        time = pick_time.get('picking_time', 0) if pick_time is not None else 0
        ratio = blocked.blocked_num / time if time > 0 else 0

        elt['blocked'] = elt['blocked'] + blocked.blocked_num
        elt['time'] = time
        elt['ratio'] = elt['ratio'] + ratio
        elt['reason'].append({
            'code': blocked.code,
            'count': blocked.blocked_num,
            'label': blocked.label
        })

    return blocked_boxes_ratio


def get_box_blocked_mean(start, end, type_mag, counter_values):
    blocked_boxes = get_box_blocked_data(start=start, end=end, type_mag=type_mag, counter_values=counter_values)

    blocked = sum(
        filter(lambda d: d > 0,
               map(lambda r: r['blocked'], blocked_boxes)))
    duration = sum(
        filter(lambda d: d > 0,
               map(lambda r: r['time'], blocked_boxes)))

    mean = round(blocked / duration, 2) if duration > 0 else None
    state = State.UNKNOW.value if mean is None or math.isnan(mean) else State.KO.value
    threshold = _get_config(property=CONTRAT_BLOCKED_BOX_PER_HOUR)

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

    return {
        'mean': mean,
        'state': state,
        'threshold': {
            'max': threshold,
            'warning': round(1.1 * threshold, 2),
            'unit': 'unit.blocked_box'
        }
    } if state != State.UNKNOW.value else None
