import json
import logging

from common.status import HTTP_200_OK, HTTP_500_INTERNAL_SERVER_ERROR
from flask import Blueprint, request, send_file
from flask_jwt_extended import jwt_required
from median.models import Seuil, Stock, ZoneMag, Zone, Dispensation, DispensationItem, Magasin, Product
from peewee import fn
from ressources.astus.utils import generate_memory_excel_file

from ressources.blueprint.threshold.threshold_service import get_all_request, get_obj_excel_threshold_v2

threshold_blueprint = Blueprint('threshold', __name__)

logger = logging.getLogger('median')


def _get_seuil_quantite(ref, fraction, poste):
    _mag = _get_mag(poste)
    if _mag == '-':
        return '-'
    _q = (
        Stock.select(fn.SUM(Stock.quantite).alias('qte'))
        .where((Stock.reference == ref) & (Stock.fraction == fraction) & (Stock.magasin == _mag)))

    stk = (
        Stock().select(fn.SUM(Stock.quantite).alias("total"))
        .join(ZoneMag, on=(Stock.magasin == ZoneMag.mag))
        .join(
            Zone, on=((Zone.zone == ZoneMag.zone) & (Zone.appro == 1))).where(
            Stock.zone_admin == poste, Stock.reference == ref, Stock.fraction == fraction))

    return (_q[0].qte or 0) + (stk[0].total or 0)


def _get_seuil_commande(ref, fraction, maxi, poste):
    _mag = _get_mag(poste)
    if _mag == '-':
        return '-'

    # calcul des quantités déjà en commande
    q = (Dispensation.select(
        (fn.SUM(DispensationItem.qte_dem - DispensationItem.qte_serv)).alias('cmd')).join(
        DispensationItem, on=(
            (Dispensation.liste == DispensationItem.liste)
            & (Dispensation.mode == DispensationItem.mode)
            & (fraction == DispensationItem.fraction)
            & (DispensationItem.reference == ref)))
         .where(  # noqa
        (Dispensation.mode == 'E')
        & (Dispensation.zone_fin == poste))
         .group_by(Dispensation.zone_fin))

    qty_in_list = 0 if not q else q[0].cmd
    if qty_in_list < 0:
        qty_in_list = 0
    return qty_in_list


def _get_mag(type_mag):
    _m = Magasin.select(Magasin.mag).where(Magasin.type_mag == type_mag)
    if len(_m) == 0:
        return '-'
    return _m[0].mag


@threshold_blueprint.route('<string:ref_pk>', methods=['GET'])
@jwt_required()
def get_all_by_reference(ref_pk):
    try:
        p = Product.select().where(Product.pk == ref_pk).get()
        ref = p.reference
        filtered_seuils_query = (
            Seuil.select().where(Seuil.reference == ref))

        filtered_seuils = (filtered_seuils_query
                           .order_by(Seuil.zone))

        logger.debug('Lines : %s.' % len(filtered_seuils))

        return {'data': [get_obj_threshold(s) for s in filtered_seuils]
                }, HTTP_200_OK

    except Exception as error:
        logger.error('Get Seuils raised an exception: ', error.args)
        return {'message': error.args}, HTTP_500_INTERNAL_SERVER_ERROR


@threshold_blueprint.route('<string:ref_pk>', methods=['PUT'])
@jwt_required()
def update(ref_pk):
    args = json.loads(request.data)
    _pk = args['pk']
    _seuil_min = args['seuil_min']
    _seuil_max = args['seuil_max']

    logger.info('Edition d\'un seuil, pk: "%s"' % (_pk))

    try:
        Seuil.update({Seuil.stock_mini: _seuil_min, Seuil.stock_maxi: _seuil_max}).where(Seuil.pk == _pk).execute()

    except Exception as error:
        logger.error(error.args)
        return error.args, 503

    logger.info('Edition réussie d\'un seuil, pk: "%s"' % (_pk))
    return 'Success'


@threshold_blueprint.route('v2/<string:threshold_pk>', methods=['PUT'])
@jwt_required()
def update_threshold(threshold_pk):
    args = json.loads(request.data)
    _seuil_min = args['mini']
    _seuil_max = args['maxi']

    logger.info('Edition d\'un seuil, pk: "%s"' % (threshold_pk))

    try:
        threshold = Seuil.select().where(Seuil.pk == threshold_pk).get()
        threshold.stock_mini = _seuil_min
        threshold.stock_maxi = _seuil_max
        threshold.save()

    except Exception as error:
        logger.error(error.args)
        return error.args, 503

    logger.info('Edition réussie d\'un seuil, pk: "%s"' % (threshold_pk))
    return 'Success'


@threshold_blueprint.route('<string:ref_pk>', methods=['POST'])
@jwt_required()
def add(ref_pk):
    logger.info("Ajout d'un seuil stock produit...")

    args = json.loads(request.data)
    _mag = args['mag']
    _fraction = args['fraction']
    _seuil_min = args['seuil_min']
    _seuil_max = args['seuil_max']

    try:
        p = Product.select().where(Product.pk == ref_pk).get()
        ref = p.reference
        # regarder si on n'a pas déjà une ligne de seuils pour ce couple ref/fraction
        meme_seuils_count = (
            Seuil.select()
            .where((Seuil.reference == ref) & (Seuil.fraction == _fraction) & (Seuil.zone == _mag))
            .count())
        if meme_seuils_count > 0:
            return {'message': 'threshold.error.exist'}, 503

        seuil = (Seuil.create(
            reference=ref,
            zone=_mag,
            stock_mini=int(_seuil_min),
            stock_maxi=int(_seuil_max),
            fraction=int(_fraction)))

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

    logger.info('Ajout d\'un seuil stock produit... REUSSI')
    return {'pk': seuil.pk}, HTTP_200_OK


@threshold_blueprint.route('<string:ref_pk>', methods=['DELETE'])
@jwt_required()
def delete(ref_pk):
    args = request.args
    _pk = args['pk']

    logger.info('Suppression d\'un seuil, pk: "%s"' % (_pk))

    try:
        Seuil.delete().where(Seuil.pk == _pk).execute()

    except Exception as error:
        logger.error(error.args)
        return error.args, 503

    logger.info("""Suppression réussie d'un seuil, pk: "%s" """ % (_pk))
    return 'Success'


@threshold_blueprint.route('v2/<string:threshold_pk>', methods=['DELETE'])
@jwt_required()
def delete_threshold(threshold_pk):
    logger.info('Suppression d\'un seuil, pk: "%s"' % (threshold_pk))

    try:
        Seuil.delete().where(Seuil.pk == threshold_pk).execute()

    except Exception as error:
        logger.error(error.args)
        return error.args, 503

    logger.info("""Suppression réussie d'un seuil, pk: "%s" """ % (threshold_pk))
    return 'Success'


def get_obj_threshold(s):
    obj = get_obj_excel_threshold(s)
    obj['pk'] = s.pk

    return obj


def get_obj_excel_threshold(s):
    return {
        'mag': s.zone,
        'fraction': s.fraction,
        'seuil_min': s.stock_mini,
        'seuil_max': s.stock_maxi,
        'quantite': _get_seuil_quantite(s.reference, s.fraction, s.zone),
        'commande': _get_seuil_commande(s.reference, s.fraction, s.stock_maxi, s.zone),
        'en_commande': (0 if _get_seuil_quantite(s.reference, s.fraction, s.zone) +
                        _get_seuil_commande(s.reference, s.fraction, s.stock_maxi, s.zone) >= s.stock_mini
                        else s.stock_maxi - _get_seuil_quantite(s.reference, s.fraction, s.zone) +
                        _get_seuil_commande(s.reference, s.fraction, s.stock_maxi, s.zone))
    }


@threshold_blueprint.route('<string:ref_pk>/export', methods=['PATCH'])
@jwt_required()
def export(ref_pk):
    data = json.loads(request.data)
    headers = data['headers']
    search = data.get('search', '')

    p = Product.select().where(Product.pk == ref_pk).get()
    ref = p.reference
    expr = (Seuil.reference == ref)

    if search != '':
        expr = expr & Seuil.zone.contains(search.strip())

    filtered_seuils_query = (
        Seuil.select().where(expr))

    filtered_seuils = (filtered_seuils_query
                       .order_by(Seuil.zone))

    memory_file = generate_memory_excel_file(headers=headers, items=filtered_seuils,
                                             transform_function=get_obj_excel_threshold)

    return send_file(memory_file, as_attachment=True, download_name="seuil.xlsx")


@threshold_blueprint.route('all', methods=['POST'])
@jwt_required()
def get_all():
    try:
        args = json.loads(request.data)
        _limit = args.get('pageSize', -1)
        _page = args.get('page', -1)

        filtered_seuils_query = get_all_request(request)

        filtered_seuils = (filtered_seuils_query
                           .limit(_limit)
                           .offset(_page * _limit)
                           .order_by(Magasin.libelle, Product.designation, Seuil.fraction))

        logger.debug('Lines : %s.' % filtered_seuils_query.count())
        logger.info(filtered_seuils_query)
        return {'list': [
            {
                'pk': item.pk,
                'reference': item.reference,
                'designation': item.designation,
                'equipment': item.equipment_designation,
                'fraction': item.fraction,
                'mini': item.mini,
                'maxi': item.maxi,
                'ordered_quantity': item.ordered_quantity,
                'command_quantity': (0 if item.stk_quantity + item.stk_admin + item.ordered_quantity >= item.mini
                                     else item.maxi - (item.stk_quantity + item.stk_admin) + item.ordered_quantity),
                'quantity': item.stk_quantity + item.stk_admin
            }
            for item in filtered_seuils.objects()],
            'count': filtered_seuils_query.count(),
        }, HTTP_200_OK

    except Exception as error:
        logger.error('Get Seuils raised an exception: ', error.args)
        return {'message': error.args}, HTTP_500_INTERNAL_SERVER_ERROR


@threshold_blueprint.route('v2/export', methods=['POST'])
@jwt_required()
def export_v2():
    data = json.loads(request.data)
    headers = data.get('headers', [])

    items = (get_all_request(request)
             .order_by(Magasin.libelle, Product.designation, Seuil.fraction)
             .objects())

    memory_file = generate_memory_excel_file(headers=headers,
                                             items=items,
                                             transform_function=get_obj_excel_threshold_v2)

    return send_file(memory_file, as_attachment=True, download_name="seuil.xlsx")
