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 Stock, Magasin, Product, Dispensation, \
    DispensationItem, Adresse
from peewee import fn
from ressources.astus.utils import generate_memory_excel_file
from ressources.blueprint.stock.stock_service import _stock_request, _stock_obj

from ressources.blueprint.stock.stock_service import _stock_quantity_request, DIRECTION, SORTBY

ref_stock_blueprint = Blueprint('ref_stock', __name__)
logger = logging.getLogger('median')


@ref_stock_blueprint.route('/count', methods=['POST'])
@jwt_required()
def count_all_stock():
    res = _stock_request(all_stock=True).count()
    return {
        "data": res
    }, HTTP_200_OK


@ref_stock_blueprint.route('/all', methods=['POST'])
@jwt_required()
def get_all_stock():
    try:
        data = json.loads(request.data)
        page = data['pageNumber']
        page_size = data['rowsPerPage']
    except Exception as ex:
        return {'message': str(ex)}, HTTP_500_INTERNAL_SERVER_ERROR

    stocks = _stock_request(all_stock=True)
    sort = data.get('sort', None)

    if sort is not None:
        if sort['id'] == SORTBY.QUANTITY.value:
            if sort['direction'] == DIRECTION.ASC.value:
                stocks = stocks.order_by(stocks.c.stock_quantity.asc())
            else:
                stocks = stocks.order_by(stocks.c.stock_quantity.desc())
        elif sort['id'] == SORTBY.DESIGNATION.value:
            if sort['direction'] == DIRECTION.ASC.value:
                stocks = stocks.order_by(stocks.c.stock_designation.asc())
            else:
                stocks = stocks.order_by(stocks.c.stock_designation.desc())

    res = (stocks
           .limit(page_size).offset(page * page_size))

    return {
        "list": [
            _stock_obj(item)
            for item in res.objects()
        ]
    }, HTTP_200_OK


@ref_stock_blueprint.route('/<string:equipment_pk>/<string:ref_pk>', methods=['PATCH'])
@jwt_required()
def get_detail_stock(equipment_pk, ref_pk):

    data = json.loads(request.data)
    fraction = data.get('fraction', 100)

    stocks = (Magasin.select(
        Product.reference.alias('stock_reference'),
        Product.designation.alias('stock_designation'),
        Stock.fraction.alias('stock_fraction'),
        Stock.quantite.alias('stock_quantity'),
        Stock.lot.alias('stock_batchNumber'),
        Stock.adresse.alias('stock_address'),
        Stock.ucd.alias('stock_ucd'),
        Stock.pk.alias('stock_pk'),
        Adresse.format.alias('format'),
        fn.IF(Stock.cip == Stock.contenant, True, False).alias('isBox'),
        Stock.contenant.alias('container_BP'),
        Stock.serial.alias('serial'),
    )
              .join(Stock, on=Stock.magasin == Magasin.mag)
              .join(Product, on=Stock.reference == Product.reference)
              .join(Adresse, on=Stock.adresse == Adresse.adresse)
              .where((Magasin.pk == equipment_pk) &
                     (Product.pk == ref_pk) &
                     (Stock.quantite > 0) &
                     (Stock.fraction == fraction))
              .order_by(Stock.adresse)
              )

    res = []

    for item in stocks.objects():
        adr = next(filter(lambda r: r['adress'] == item.stock_address, res), None)

        if adr is None:
            adr = {
                'adress': item.stock_address,
                'references': []
            }
            res.append(adr)

        ref = next(filter(lambda r: r['reference'] == item.stock_reference, adr['references']), None)

        if ref is None or ref['isBox']:
            ref = {
                'reference': item.stock_reference,
                'fraction': item.stock_fraction,
                'batchNumber': item.stock_batchNumber,
                'quantity': item.stock_quantity,
                'container': item.container_BP,
                'isBox': item.isBox,
                'format': item.format,
                'ucd': item.stock_ucd,
                'nbElt': 1,
                'serial': item.serial,
                'pk': item.stock_pk
            }
            adr['references'].append(ref)
        else:
            ref['nbElt'] = ref['nbElt'] + 1
            ref['quantity'] = ref['quantity'] + item.stock_quantity

    return {"data": res}, HTTP_200_OK


@ref_stock_blueprint.route('/<string:equipment_pk>/<string:ref_pk>/ordered', methods=['PATCH'])
@jwt_required()
def get_detail_ordered(equipment_pk, ref_pk):

    data = json.loads(request.data)
    fraction = data.get('fraction', 100)

    items = (Dispensation
             .select(Dispensation.pk.alias('list_pk'),
                     Dispensation.liste.alias('list_label'),
                     Dispensation.date_creation.alias('list_creation'),
                     Dispensation.etat.alias('list_etat'),
                     Dispensation.zone_fin.alias('list_equipment'),
                     (fn.SUM(DispensationItem.qte_dem - DispensationItem.qte_serv)).alias('quantity'))
             .join(DispensationItem, on=((Dispensation.liste == DispensationItem.liste) &
                                         (Dispensation.mode == DispensationItem.mode) &
                                         (DispensationItem.fraction == fraction)))
             .join(Magasin, on=Dispensation.zone_fin == Magasin.type_mag)
             .join(Product, on=Product.reference == DispensationItem.reference)
             .where((Dispensation.mode == 'E') &
                    (Product.pk == ref_pk) &
                    (Magasin.pk == equipment_pk))
             .group_by(Dispensation.liste)
             .having((fn.SUM(DispensationItem.qte_dem - DispensationItem.qte_serv)) > 0)
             )

    return {
        'list': [
            {
                "pk": item.list_pk,
                "label": item.list_label,
                "date_creation": item.list_creation,
                "state": item.list_etat,
                'mag': item.list_equipment,
                "quantity": item.quantity
            } for item in items.objects()
        ]
    }, HTTP_200_OK


@ref_stock_blueprint.route('/<string:equipment_pk>/container/<string:stock_pk>', methods=['GET'])
@jwt_required()
def get_stock_container(equipment_pk, stock_pk):
    container = (Stock.select(Stock.pk,
                              Stock.quantite.alias('quantity'),
                              Stock.serial.alias('serial'),
                              Stock.lot.alias('batchNumber'))
                 .join(Magasin, on=Magasin.mag == Stock.magasin)
                 .where((Stock.pk == stock_pk) &
                        (Stock.quantite > 0) &
                        (Magasin.pk == equipment_pk)))

    return {
        "list": [
            {
                "pk": item.pk,
                "serial": item.serial,
                "quantity": item.quantity,
                "batchNumber": item.batchNumber
            } for item in container
        ]
    }, HTTP_200_OK


@ref_stock_blueprint.route('/outstock', methods=['POST'])
@jwt_required()
def get_stock_external():
    data = json.loads(request.data)
    page = data['pageNumber']
    page_size = data['rowsPerPage']
    stocks = _stock_request()
    res = (stocks
           .order_by(stocks.c.stock_quantity.asc(), stocks.c.stock_designation.asc())
           .limit(page_size).offset(page * page_size))

    return {
        "list": [
            _stock_obj(item)
            for item in res.objects()
        ]
    }, HTTP_200_OK


def get_obj_excel_stock(item):
    return {
        "equipment_label": item.equipment_label,
        "stock_fraction": item.stock_fraction,
        "stock_reference": item.stock_reference,
        "stock_label": item.stock_designation,
        "stock_quantity": item.stock_quantity,
        "threshold_maxi": item.stock_maxi,
        "threshold_mini": item.stock_mini,

    }


@ref_stock_blueprint.route('export', methods=['POST'])
@jwt_required()
def export():
    data = json.loads(request.data)
    headers = data['headers']

    stocks = _stock_request(all_stock=True)
    res = (stocks
           .order_by(stocks.c.stock_quantity.asc(), stocks.c.stock_designation.asc()))

    memory_file = generate_memory_excel_file(headers=headers, items=res.objects(),
                                             transform_function=get_obj_excel_stock)

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


def get_obj_excel_stock_quantity(item):
    return {
        "equipment_label": item.equipment_label,
        "stock_reference": item.stock_reference,
        "stock_label": item.stock_designation,
        "stock_quantity": item.stock_quantity,
    }


@ref_stock_blueprint.route('export/quantity', methods=['POST'])
@jwt_required()
def export_quantity():
    data = json.loads(request.data)
    headers = data['headers']

    stocks = _stock_quantity_request()
    res = (stocks
           .order_by(Product.reference.asc(), fn.SUM(Stock.quantite * (Stock.fraction / 100)).asc()))

    memory_file = generate_memory_excel_file(headers=headers, items=res.objects(),
                                             transform_function=get_obj_excel_stock_quantity)

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