import json
import math
import operator
import os
import uuid
from datetime import datetime
from functools import reduce

import pandas as pd
from median.models import Product, Ucd, Gtin
from ressources.blueprint.product_status import Status
from flask import Blueprint, request, session
from flask_jwt_extended import jwt_required
from peewee import DoesNotExist
from common.status import HTTP_200_OK, HTTP_204_NO_CONTENT
from common.models import WebImportRefHeader, WebImportRefLine

importref_blueprint = Blueprint('importref', __name__)


@importref_blueprint.route('all', methods=['POST'])
@jwt_required()
def get_all():
    data = json.loads(request.data)
    user_id = session['user_id']
    _criterias = data.get('criterias', [])
    _states = data.get('states', [])

    andexpr = ((WebImportRefHeader.user_pk == user_id) &
               (WebImportRefHeader.end.is_null()))

    if len(_criterias) > 0:
        lst = list(map(lambda s: (
            (WebImportRefLine.designation.contains(s.strip())) |
            (WebImportRefLine.reference.contains(s.strip()))
        ), _criterias))
        search = reduce(operator.and_, lst)

        expr = reduce(operator.and_, [andexpr, search])
    else:
        expr = andexpr

    req = (WebImportRefLine.select(WebImportRefLine.pk.alias('pk'),
                                   WebImportRefLine.reference.alias('reference'),
                                   WebImportRefLine.designation.alias('designation'),
                                   WebImportRefLine.cip.alias('cip'),
                                   WebImportRefLine.ucd.alias('ucd'),
                                   WebImportRefLine.blister_qty.alias('blister'),
                                   WebImportRefLine.box_qty.alias('box'))
           .join(WebImportRefHeader).where(expr))

    data = []

    findError = len(list(filter(lambda s: s == 1, _states))) > 0
    findOk = len(list(filter(lambda s: s == 0, _states))) > 0

    for item in req.objects():
        res = _check(item)

        if len(_states) == 0 or (findError and res[1]) or (findOk and not res[1]):
            data.append(res[0])

    return {'list': data}


@importref_blueprint.route('close', methods=['POST'])
@jwt_required()
def close():
    user_id = session['user_id']
    header = WebImportRefHeader.select().where((WebImportRefHeader.user_pk == user_id) &
                                               (WebImportRefHeader.end.is_null())).get()
    header.end = datetime.utcnow()
    header.save()

    return {}, HTTP_204_NO_CONTENT


@importref_blueprint.route('save', methods=['POST'])
@jwt_required()
def save():
    user_id = session['user_id']
    req = (WebImportRefLine.select(WebImportRefLine.pk.alias('pk'),
                                   WebImportRefLine.reference.alias('reference'),
                                   WebImportRefLine.designation.alias('designation'),
                                   WebImportRefLine.cip.alias('cip'),
                                   WebImportRefLine.ucd.alias('ucd'),
                                   WebImportRefLine.blister_qty.alias('blister'),
                                   WebImportRefLine.box_qty.alias('box'))
           .join(WebImportRefHeader).where((WebImportRefHeader.user_pk == user_id) &
                                           (WebImportRefHeader.end.is_null())))

    count = 0

    for item in req.objects():
        if import_line(item):
            count = count + 1

    header = WebImportRefHeader.select().where((WebImportRefHeader.user_pk == user_id) &
                                               (WebImportRefHeader.end.is_null())).get()
    header.end = datetime.utcnow()
    header.save()

    return {'count': count}, HTTP_200_OK


def import_line(item):
    res = _check(item)

    if res[1]:
        return False

    reference = item.reference
    try:
        product = Product \
            .select().where(Product.reference == reference) \
            .get()
    except DoesNotExist:
        product = Product()

    product.designation = item.designation
    product.reference = reference
    product.save()

    if item.ucd is not None:
        try:
            ucd = Ucd.select() \
                .where(Ucd.ucd == item.ucd) \
                .get()
        except DoesNotExist:
            ucd = Ucd()
            ucd.ucd = item.ucd
            ucd.reference = reference
            ucd.save()

        if item.cip is not None:
            try:
                Gtin.select() \
                    .where((Gtin.cip == item.cip) &
                           (Gtin.dossier == '0') &
                           (Gtin.ucd == ucd.ucd)).get()
            except DoesNotExist:
                gtin = Gtin()
                gtin.ucd = ucd.ucd
                gtin.cip = item.cip
                gtin.qt_blister = item.blister
                gtin.qt_boite = item.box
                gtin.save()

    return True


@importref_blueprint.route('<string:line_pk>/check', methods=['GET'])
@jwt_required()
def check_line(line_pk):
    item = _getLine(line_pk)
    return {'data': _check(item)[0]}, HTTP_200_OK


def _getLine(pk):
    user_id = session['user_id']
    expr = ((WebImportRefHeader.user_pk == user_id) &
            (WebImportRefHeader.end.is_null()) &
            (WebImportRefLine.pk == pk))

    item = (WebImportRefLine.select(WebImportRefLine.pk.alias('pk'),
                                    WebImportRefLine.reference.alias('reference'),
                                    WebImportRefLine.designation.alias('designation'),
                                    WebImportRefLine.cip.alias('cip'),
                                    WebImportRefLine.ucd.alias('ucd'),
                                    WebImportRefLine.blister_qty.alias('blister'),
                                    WebImportRefLine.box_qty.alias('box'))
            .join(WebImportRefHeader).where(expr)).get()

    return item


def _check(line):
    res = {
        "pk": line.pk,
        "reference": {
            "status": Status.ERROR.value,
            "msg": [],
            'value': line.reference
        },

        "designation": {
            "status": Status.OK.value,
            "msg": [],
            'value': line.designation
        },
        "cdu": {
            "status": Status.ERROR.value,
            "msg": [],
            "value": line.ucd
        },
        "gtin": {
            "status": Status.ERROR.value,
            "msg": [],
            "value": line.cip
        },
        "blister": {
            "status": Status.OK.value,
            "msg": [],
            "value": line.blister
        },
        "box": {
            "status": Status.OK.value,
            "msg": [],
            "value": line.box
        },
    }

    ref = line.reference
    arg_ucd = line.ucd
    qte_boite = line.box
    qte_bliter = line.blister
    cip = line.cip
    is_error = False

    try:
        product = Product \
            .select(Product.reference, Product.designation, Product.dci).where(Product.reference == ref) \
            .get()
        res['reference']['status'] = Status.OK.value
        if product.designation != line.designation:  # or product.dci != args.get('inn', ''):
            res['designation']['status'] = Status.UPDATE.value
            res['designation']['msg'].append({"msg": "importref.reference.update.designation", "params": []})
        # if product.dci != args.get('inn', ''):
        #     res['inn'] = Status.UPDATE.value
        #     res['inn_msg'].append({"msg": "importref.reference.update.inn", "params": []})
    except DoesNotExist:
        res['reference']['status'] = Status.CREATE.value
        res['reference']['msg'].append({"msg": "importref.reference.create", "params": []})
    try:
        ucd = Ucd.select(Ucd.reference) \
            .where(Ucd.ucd == arg_ucd) \
            .get()

        if ucd.reference == ref:
            res['cdu']['status'] = Status.OK.value
        else:
            res['cdu']['status'] = Status.ERROR.value
            is_error = True
            res['cdu']['msg'].append({"msg": "importref.cdu.error", "params": []})
    except DoesNotExist:
        res['cdu']['status'] = 'create'
        res['cdu']['msg'].append({"msg": "importref.cdu.create", "params": []})

    try:
        gtin = Gtin.select(Gtin.ucd, Gtin.qt_blister, Gtin.qt_boite) \
            .where((Gtin.cip == cip) & (Gtin.dossier == '0')).get()
        if gtin.ucd != arg_ucd:
            res['gtin']['status'] = Status.ERROR.value
            is_error = True
            if gtin.ucd != arg_ucd:
                res['gtin']['msg'].append({"msg": "importref.gtin.error.ucd", "params": []})

        elif gtin.qt_blister != qte_bliter or gtin.qt_boite != qte_boite:
            res['gtin']['status'] = Status.OK.value
            if gtin.qt_blister != qte_bliter:
                res['blister']['status'] = Status.ERROR.value
                is_error = True
                res['blister']['msg'].append(
                    {"msg": "importref.gtin.update.sub", "params": [qte_bliter, gtin.qt_blister]})
            if gtin.qt_boite != qte_boite:
                res['box']['status'] = Status.ERROR.value
                is_error = True
                res['box']['msg'].append({"msg": "importref.gtin.update.box", "params": [gtin.qt_boite, qte_boite]})
        else:
            res['gtin']['status'] = Status.OK.value
    except DoesNotExist:
        res['gtin']['status'] = Status.CREATE.value
        res['gtin']['msg'].append({"msg": "importref.gtin.create", "params": []})

    if qte_boite == 0:
        res['box']['status'] = Status.ERROR.value
        is_error = True
        res['box']['msg'].append({"msg": "importref.gtin.error.box", "params": []})
    return res, is_error


@importref_blueprint.route('import', methods=['PUT'])
@jwt_required()
def import_file():
    for fname in request.files:
        f = request.files.get(fname)
        user_id = session['user_id']
        file_name = f'{uuid.uuid4()}.xlsx'

        directory = os.sep.join(
            [os.getcwd(), "tmp_export"])
        file = f'{directory}\\{file_name}'

        try:
            header = WebImportRefHeader.select().where((WebImportRefHeader.user_pk == user_id) &
                                                       (WebImportRefHeader.end.is_null())).get()
            header.end = datetime.utcnow()
            header.save()
            header = WebImportRefHeader()
        except DoesNotExist:
            header = WebImportRefHeader()

        header.start = datetime.utcnow()
        header.user_pk = user_id
        header.save()

        try:
            f.save(file)

            df = pd.read_excel(file, sheet_name=0, header=None)
            num_line = 1
            reports = []
            for item in df.values.tolist():
                if num_line != 1:
                    report = {
                        'line': num_line,
                        'errors': [],

                    }
                    line = WebImportRefLine()
                    line.header = header
                    try:

                        line.reference = str(item[0])
                        line.designation = item[1]
                        line.ucd = str(item[2]) if str(item[2]) != "nan" else ''
                        line.cip = str(item[3]) if str(item[3]) != "nan" else ''
                        line.box_qty = int(item[4]) if not math.isnan(int(item[4])) else None
                        line.blister_qty = int(item[5]) if not math.isnan(int(item[5])) else None
                    except Exception:
                        report['errors'].append('import_ref.error.column_number')
                    line.save()
                    reports.append(report)
                num_line += 1

        finally:
            if os.path.exists(file):
                os.remove(file)

        return {
            'list': [{

                'errors': item['errors']} for item in reports]}, HTTP_200_OK
