import operator

import datetime
from functools import reduce
import logging
from flask import send_file
import os
import uuid
from flask import Blueprint, request
from flask_jwt_extended import jwt_required
from median.models import Magasin, Stock, Product, Adresse
from median.views import RawConfig
from peewee import JOIN
import json

from ressources.astus.utils import generate_excel_file

astus_stock_blueprint = Blueprint('astus_stock', __name__)
logger = logging.getLogger('median.webserver')


def get_all_stocks(v_search_list, buffer, expired):

    nb_peremption = RawConfig().read('k_eco_nb_péremption_astus')
    nb_peremption = nb_peremption.value if nb_peremption is not None else 10
    date_max = (datetime.datetime.now() + datetime.timedelta(days=nb_peremption)).strftime('%Y-%m-%d')

    andexpr = (Magasin.eco_type == 'A') & \
              (((buffer is False) & (expired is False)) |
               (((buffer is True) & (Stock.zone_admin == 'TMP')) |
                ((expired is True) & (Stock.date_peremption < date_max))))

    if len(v_search_list) > 0:
        lst = list(map(lambda s: (
            (Product.designation.contains(s.strip())) |
            (Magasin.libelle.contains(s.strip())) |
            (Stock.contenant.contains(s.strip())) |
            (Stock.adresse.contains(s.strip())) |
            (Stock.reference.contains(s.strip())) |
            (Stock.lot.contains(s.strip())) |
            (Adresse.format.contains(s.strip()))
        ), v_search_list))
        search = reduce(operator.and_, lst)
        expr = reduce(operator.and_, [andexpr, search])
    else:
        expr = andexpr

    stocks = Stock.select(Stock.pk, Stock.zone_admin, Stock.lot,
                          Stock.reference, Stock.contenant,
                          Stock.date_peremption, Stock.date_entree, Stock.quantite,
                          Stock.adresse.alias('adr'),
                          Adresse.format, Product.designation, Magasin.libelle) \
        .join(Magasin, on=(Magasin.mag == Stock.magasin)) \
        .switch(Stock) \
        .join(Product, JOIN.LEFT_OUTER, on=(Product.reference == Stock.reference)) \
        .switch(Stock) \
        .join(Adresse, JOIN.LEFT_OUTER, on=(Stock.adresse == Adresse.adresse)) \
        .where(expr)

    return stocks


def getObjStock(stk):
    obj = {
        'pk': stk.pk,
        'magasin': stk.magasin.libelle,
        'zone_admin': stk.zone_admin,
        'adress': stk.adr,
        'reference': stk.reference,
        'batch_number': stk.lot,
        'quantity': stk.quantite,
        'container': stk.contenant,
        'expiration_date': stk.date_peremption,
        'entry_date': stk.date_entree,
    }
    if hasattr(stk, 'product'):
        obj['designation'] = stk.product.designation

    if (hasattr(stk, 'adresse')) & (stk.adresse is not None):
        obj['format'] = stk.adresse.format

    return obj


@astus_stock_blueprint.route('/export', methods=['PATCH'])
@jwt_required()
def export_stock():
    data = json.loads(request.data)

    v_search_list = data['search']
    headers = data['headers']
    buffer = data['buffer'] if hasattr(data, 'buffer') else False
    expired = data['expired'] if hasattr(data, 'expired') else False

    name = os.sep.join(
        [os.getcwd(), "tmp_export", "%s.xlsx" % uuid.uuid4()])

    stocks = get_all_stocks(v_search_list=v_search_list, buffer=buffer, expired=expired)
    generate_excel_file(name, headers, stocks, getObjStock)

    return send_file(name, as_attachment=True)


@astus_stock_blueprint.route('/list', methods=['PATCH'])
def get_stock():
    data = json.loads(request.data)

    v_search_list = data['search']
    buffer = data['buffer']
    expired = data['expired']

    stocks = get_all_stocks(v_search_list=v_search_list, buffer=buffer, expired=expired)

    count = stocks.count()

    stocks = stocks.limit(int(data.get('length'))) \
        .offset(int(data.get('start'))) \
        .order_by(Product.designation, Stock.adresse)

    list_stock = []
    for stock in stocks:
        obj = getObjStock(stock)
        list_stock.append(obj)

    return {
        'total': count,
        'data': list_stock
    }, 200
