import datetime
import json
import logging
import math

from flask import Blueprint, request, jsonify, session
from flask_jwt_extended import jwt_required, get_jwt_identity
from median.constant import TypeListe, EtatListe

from peewee import JOIN, DoesNotExist
from median.models import FListe, FListeError, Magasin, FItem, Product, ListeItemModel, Service, User
from common.status import HTTP_200_OK, HTTP_400_BAD_REQUEST, HTTP_404_NOT_FOUND

from common.log import log_riedl

from common.status import HTTP_204_NO_CONTENT
from ressources.acced.acced_replenish_blueprint import _transfert_service
from ressources.riedls.riedl_replenish_service import get_cancel_or_replace, riedl_replenish

riedl_input_blueprint = Blueprint('riedl_input', __name__)

logger = logging.getLogger('median')


@riedl_input_blueprint.errorhandler(400)
def handle_error(err):
    """
    When an error 400, return a formated custom error on the frontend
    """
    headers = err.data.get("headers", None)
    messages = err.data.get("messages", ["Invalid request."])
    if headers:
        return jsonify({"message": messages}), err.code, headers
    else:
        return jsonify({"message": messages}), err.code


@riedl_input_blueprint.route('lists', methods=['POST'])
@jwt_required()
def getList():
    data = json.loads(request.data)

    try:
        equipments = data.get('equipments', [])
        states = data.get('states', [])
        search_list = data.get('criterias', [])

        andexpr = (FListe.mode == TypeListe.Input.value) & \
                  (FListe.zone_deb == 'RIEDL') & \
                  (Magasin.eco_type == 'L')

        if any(equipments):
            andexpr = andexpr & (Magasin.pk << equipments)

        if any(states):
            andexpr = andexpr & (FListe.etat << states)

        list_input = FListe.select(FListe.liste, FListe.type_servi, FListe.date_creation,
                                   FListe.etat, FListe.pk, FListe.mode, FListe.zone_fin,
                                   FListeError.message.alias('error_message'),
                                   Magasin.mag, Magasin.libelle, Magasin.type_mag,
                                   Product.designation, Product.reference, Service.libelle) \
            .join(FListeError, JOIN.LEFT_OUTER,
                  on=(FListe.liste == FListeError.liste) & (FListe.mode == FListeError.mode)) \
            .switch(FListe) \
            .join(Magasin, on=Magasin.type_mag == FListe.zone_fin) \
            .join(FItem, JOIN.LEFT_OUTER, on=FItem.liste == FListe.liste) \
            .switch(FListe) \
            .join(Product, JOIN.LEFT_OUTER, on=Product.reference == FItem.reference) \
            .switch(FListe) \
            .join(Service, JOIN.LEFT_OUTER, on=Service.code == FListe.service) \
            .where(andexpr)

        res = __create_list(list_input)

        filtered = []
        if len(search_list) > 0:
            for m in res:

                search_res = list(map(lambda s: m['text'].find(s.strip()) != -1 or any(
                    list(filter(lambda p: p['designation'].find(s.strip()) != -1 or p['ref'].find(s.strip()) != -1,
                                m['products']))),
                                      search_list))

                if all(search_res):
                    filtered.append(m)

        else:
            filtered = res

        return {'lists': filtered}, HTTP_200_OK

    except Exception as error:
        logger.error(error.args)
        return {'message': error.args}, HTTP_400_BAD_REQUEST


def __create_list(list_input):
    res = []
    for m in list_input:

        lst = list(filter(lambda f: (f['id'] == m.pk), res))
        if not any(lst):
            elt = {
                'text': m.liste,
                'id': m.pk,
                'state': m.etat,
                'mode': m.mode,
                'riedl': m.zone_fin,
                'ward': m.service.libelle if hasattr(m, 'service') and m.service is not None else '',
                'message': m.flisteerror.error_message
                if hasattr(m, 'flisteerror') and m.flisteerror is not None else '',
                'wardType': m.type_servi,
                'products': [],
                'creationDate': datetime.datetime.utcfromtimestamp(m.date_creation.timestamp()),
                'equipment': {
                    'code': m.magasin.mag,
                    'type': m.magasin.type_mag,
                    'label': m.magasin.libelle,
                }
            }
            res.append(elt)
        else:
            elt = lst[0]

        if hasattr(m, 'product'):
            elt['products'].append({
                'ref': m.product.reference,
                'designation': m.product.designation,
            })
    return res


@riedl_input_blueprint.route('<string:listlabel>', methods=['GET'])
@jwt_required()
def get_item_list(listlabel):
    items = FItem.select(
        FItem.reference, Product.designation, FItem.etat, Product.pk,
        FItem.info, FItem.qte_dem, FItem.qte_serv, FItem.lot,
        Product.cip, FItem.item, FItem.pk, FItem.tperemp
    ).join(
        Product, on=FItem.reference == Product.reference
    ).where(
        FItem.liste == listlabel
    ).order_by(FItem.item)

    return {'list': [{
        'pk': i.pk,
        'etat': i.etat,
        'reference': i.reference,
        'designation': i.product.designation,
        'reference_pk': i.product.pk,
        'qte_demandee': i.qte_dem,
        'qte_servie': i.qte_serv,
        'item': i.item,
        'batch': i.lot,
        'cip': i.info if i.info is not None and len(i.info) == 13 else i.product.cip,
        'peremp': i.tperemp
    } for i in items]}, HTTP_200_OK


@riedl_input_blueprint.route('requested/<string:itemId>', methods=['PUT'])
@jwt_required()
def update_requested_qty(itemId):
    """Update the quantity on an item"""

    data = json.loads(request.data)

    qty = data['qty']

    if not math.isnan(int(qty)) and int(qty) > 0:
        item = FItem.select().where(FItem.pk == itemId).get()
        item.qte_dem = qty
        item.save()

        return {'result': 'OK'}, HTTP_200_OK
    else:
        return {'message': 'riedl.input.update.requested.zero'}, HTTP_400_BAD_REQUEST


@riedl_input_blueprint.route('', methods=['DELETE'])
def delete():
    """Delete list by name"""

    args = request.args
    list_name = args['liste']
    logger.info("Delete riedl list: %s" % list_name)
    # Check if list is relaed to a Riedl
    try:
        # Delete a liste, delete the items
        lst = FListe.get(mode=TypeListe.Input.value, liste=list_name)
        lst.delete_instance()  # Use this method to delete f_liste_error, f_item and f_item_w

    except DoesNotExist:
        logger.error('Liste %s does not exists' % (list_name,))
        return {'message': 'Liste %s does not exists' % (list_name,)}, HTTP_404_NOT_FOUND
    logger.info("Delete riedl list: %s -> Done" % list_name)
    return {'liste': args['liste']}, HTTP_200_OK


@riedl_input_blueprint.route('<string:list_pk>/<string:item_pk>', methods=['DELETE'])
@jwt_required()
def deleteItem(list_pk, item_pk):
    try:
        itm = (ListeItemModel.select(ListeItemModel)
               .join(FListe, on=ListeItemModel.liste == FListe.liste)
               .where((FListe.pk == list_pk) & (ListeItemModel.pk == item_pk))).get()

        logger.info("RIEDL: cancel item [%s]: f_item.pk %i" % (itm.item, itm.pk))
        log_riedl(
            session['username'], 'out_del_item',
            'suppression item %s (%s) liste %s ' % (itm.item, itm.pk, itm.liste)
        )
        itm.etat = EtatListe.Annuler.value
        itm.save()

        return {'result': 'ok'}, HTTP_200_OK

    except Exception as error:
        logger.error(error.args)
        return {'message': error.args}, HTTP_400_BAD_REQUEST


@riedl_input_blueprint.route('<string:equipment_pk>/replenishment', methods=['POST'])
@jwt_required()
def create_replenishment(equipment_pk):

    data = json.loads(request.data)
    orders = data.get('orders', [])

    today = datetime.datetime.now()

    equipment = Magasin.select().where(Magasin.pk == equipment_pk).get()
    identity = get_jwt_identity()
    usr = User.get(User.pk == identity)

    liste = f'{equipment.mag}-RANGEMENT-{today.strftime("%Y%m%d-%H%M%S")}-'

    k_annule_remplace = get_cancel_or_replace()

    riedl_replenish(gpao=k_annule_remplace, liste=liste, mag=equipment,
                    service=_transfert_service(), lines=orders, username=usr.username)

    return {}, HTTP_204_NO_CONTENT
