import json
from datetime import datetime, timedelta

from common.status import HTTP_200_OK, HTTP_500_INTERNAL_SERVER_ERROR
from dateutil.relativedelta import relativedelta
from flask import Blueprint, request
from flask_jwt_extended import jwt_required
from median.constant import EcoType
from median.models import Magasin, Product, Adresse, Cip, Ucd, UnitDose
from median.utils import logger
from peewee import DoesNotExist, fn

from ressources.astus.astus_filled_blueprint import CipValid, UcdValid
from ressources.blueprint.external.external_service import input_product, output_product

external_blueprint = Blueprint('external', __name__)


def get_all():
    mag_list = []
    ms = Magasin.select(
        Magasin.avatar,
        Magasin.pk, Magasin.mag, Magasin.type_mag, Magasin.libelle, Magasin.type_machine,
        Magasin.eco_type
    ).where((Magasin.eco_type == EcoType.Externe.value)).order_by(Magasin.type_mag)

    for m in ms:
        res = {
            'pk': m.pk,
            'mag': m.mag,
            'type_mag': m.type_mag,
            'eco_type': m.eco_type,
            'libelle': m.libelle,
            'type_machine': m.type_machine,
            'avatar': m.avatar,
        }
        mag_list.append(res)

    return mag_list


@external_blueprint.route('/<string:equipment_pk>/output', methods=['POST'])
@jwt_required()
def output(equipment_pk):
    data = json.loads(request.data)

    mag = Magasin.get(pk=equipment_pk)

    # k_ua_pec = Config.select(Config.value).where(Config.propriete == 'k_ua_pec').get()
    # k_ua_entree = Config.select(Config.value).where(Config.propriete == 'k_ua_entree').get()

    reqCip = Cip.select() \
        .join(Ucd, on=(Cip.ucd == Ucd.ucd)) \
        .where((Cip.cip == data['selectedCIP']) &
               (Ucd.reference == data['reference']))

    if reqCip.count() > 0:
        ucd = reqCip.first()
    else:
        return {
            'message': "external.scan.med.wrong.cip",
        }, HTTP_500_INTERNAL_SERVER_ERROR

    expiration = datetime.strptime(data['selectedExpirationDate'], '%Y-%m-%d').date()

    message = output_product(expiration=expiration, mag=mag, data=data, ucd=ucd, mvt_qty=int(data['quantity']))

    if message is None:
        return {
        }, HTTP_200_OK
    else:
        return {
            'message': message,
        }, HTTP_500_INTERNAL_SERVER_ERROR


@external_blueprint.route('/<string:equipment_pk>/supply', methods=['POST'])
@jwt_required()
def supply(equipment_pk):
    data = json.loads(request.data)

    mag = Magasin.get(pk=equipment_pk)

    # k_ua_pec = Config.select(Config.value).where(Config.propriete == 'k_ua_pec').get()
    # k_ua_entree = Config.select(Config.value).where(Config.propriete == 'k_ua_entree').get()
    cip = data.get('selectedCIP', None)
    ucd = ''
    if cip is not None:
        reqCip = Cip.select() \
            .join(Ucd, on=(Cip.ucd == Ucd.ucd)) \
            .where((Cip.cip == cip) &
                   (Ucd.reference == data['reference']))

        if reqCip.count() > 0:
            ucd = reqCip.first()
        else:
            return {
                'message': "external.scan.med.wrong.cip",
            }, HTTP_500_INTERNAL_SERVER_ERROR

    expiration = datetime.strptime(data['selectedExpirationDate'], '%Y-%m-%d').date()
    input_product(expiration=expiration, mag=mag, data=data, ucd=ucd, mvt_qty=int(data['quantity']))
    return {
    }, HTTP_200_OK


@external_blueprint.route('/<string:equipment_pk>', methods=['POST'])
@jwt_required()
def test_location(equipment_pk):
    data = json.loads(request.data)
    code = data.get('code', '')
    try:

        location = (Adresse.select(Adresse.pk, Adresse.adresse)
                    .join(Magasin, on=Magasin.mag == Adresse.magasin)
                    .where((Adresse.adresse == code) &
                           (Magasin.pk == equipment_pk)).get())
        return {
            "data": {
                'pk': location.pk,
                'label': location.adresse
            }
        }, HTTP_200_OK
    except DoesNotExist:
        return {
            "message": "external.location.error.wrong_code"
        }, HTTP_500_INTERNAL_SERVER_ERROR


@external_blueprint.route('/verifmed', methods=['GET'])
@jwt_required()
def verif_med():
    data = request.args
    cip = data.get('cip')
    expiration_date = data.get('expiration_date')
    batch = data.get('batch')
    serial = data.get('serial')

    expiration = datetime.strptime(expiration_date, '%Y-%m-%d')

    if expiration < datetime.now():
        return {
            'message': 'external.scanmed.error.expired',
        }, HTTP_500_INTERNAL_SERVER_ERROR

    product_ref = CipValid(cip)
    if product_ref == '':
        return {
            'message': 'external.scan.med.error.unknown.product',
        }, HTTP_500_INTERNAL_SERVER_ERROR

    return {
        'reference': product_ref,
        'batch_number': batch,
        'serial': serial,
        'expirationDate': expiration_date,
        'cip': cip,
    }, HTTP_200_OK


@external_blueprint.route('/all', methods=['GET'])
@jwt_required()
def get_all_external():
    try:
        return {'data': get_all()}, 200
    except Exception as error:
        logger.error('erreur recuperation magasin')
        return {'message': error.args}, 400


@external_blueprint.route('/testmed', methods=['GET'])
@jwt_required()
def test_med():
    data = request.args
    codeScann = data.get('code')

    CIP13 = ''
    UCD7 = ''
    UCD13 = ''
    NumSer = ''
    DateExp = ""
    NumLot = ""

    DebCIP13 = codeScann.find('0103400')
    DebUCD13 = codeScann.find('0203400')
    DebUCD7 = codeScann.find('0200000')

    # On n'a ni trouvé le début d'un code CIP13, ni un UCD13 ou 7 dans le code datamatrix
    if DebCIP13 == - 1 & DebUCD13 == - 1 & DebUCD7 == - 1:
        return {
            'alertMessage': 'external.scanmed.error.unknown',
        }, HTTP_500_INTERNAL_SERVER_ERROR

    codeScann = codeScann.replace('\n', '')

    if DebCIP13 > 0:  # Pour gérer les codes datamatrix qui ne respectent pas la norme GS1 ]d2 au début du code
        vbCutString = codeScann[DebCIP13:len(codeScann) - DebCIP13]
    elif DebUCD13 > 0:
        vbCutString = codeScann[DebUCD13: len(codeScann) - DebUCD13]
    elif DebUCD7 > 0:
        vbCutString = codeScann[DebUCD7: len(codeScann) - DebUCD7]
    else:
        vbCutString = codeScann

    if DebCIP13 != -1:
        CIP13 = vbCutString[3: 13 + 3]
        ref = CipValid(CIP13)

    elif DebUCD13 != -1:
        UCD13 = vbCutString[3: 13 + 3]
        ref = UcdValid(UCD13)
    else:
        UCD7 = vbCutString[9: 7 + 9]
        ref = UcdValid(UCD7)

    if ref == '':
        return {
            'alertMessage': 'external.scanmed.error.wrong.barcode',
        }, HTTP_500_INTERNAL_SERVER_ERROR

    while len(vbCutString) > 1:
        vbCodeEnCours = vbCutString[0: 2]
        if vbCodeEnCours == "00":
            vbCutString = vbCutString[20:]
        elif vbCodeEnCours == "01":  # code cip
            vbCutString = vbCutString[16:]
        elif vbCodeEnCours == "02":  # code ucd
            vbCutString = vbCutString[16:]
        elif vbCodeEnCours == "11":  # date de production
            vbCutString = vbCutString[8:]
        elif vbCodeEnCours == "15":  # utiliser avant
            try:
                vbCutString = vbCutString[8:]
            except Exception:
                vbCutString = vbCutString[len(vbCutString):]

        elif vbCodeEnCours == "17":
            # Date de péremption
            DateTMP = vbCutString[2: 8]
            year = DateTMP[0:2]
            month = DateTMP[2:4]
            date = DateTMP[4:7]

            if date != "00":
                DateExp = datetime.strptime(DateTMP, '%y%m%d')
            else:
                DateTMP = f'{year}{month}01'

                DateExp = datetime.strptime(DateTMP, '%y%m%d')

                DateExp = DateExp + relativedelta(months=1) - timedelta(days=1)

            vbCutString = vbCutString[8:]
        elif vbCodeEnCours == "37":  # fraction
            if vbCutString.find(chr(29)) != -1:
                LongNumFraction = vbCutString.find(chr(29)) - 2
                vbCutString = vbCutString[LongNumFraction + 3:]
            elif vbCutString.find(chr(255)) != -1:
                LongNumFraction = vbCutString.find(chr(255)) - 3
                vbCutString = vbCutString[LongNumFraction + 4:]
            else:
                LongNumFraction = len(vbCutString) - 2
                vbCutString = vbCutString[LongNumFraction + 2:]
        elif vbCodeEnCours == "91":  # num série ECO-DEX
            if vbCutString.find(chr(29)) != -1:
                LongNumSerie = vbCutString.find(chr(29)) - 2
                NumSer = vbCutString[2: LongNumSerie+2]
                vbCutString = vbCutString[LongNumSerie + 3:]
            elif vbCutString.find(chr(255)) != -1:
                LongNumSerie = vbCutString.find(chr(255)) - 3
                NumSer = vbCutString[3: LongNumSerie+3]
                vbCutString = vbCutString[LongNumSerie + 4:]
            else:
                LongNumSerie = len(vbCutString) - 2
                NumSer = vbCutString[2: LongNumSerie+2]
                vbCutString = vbCutString[LongNumSerie + 2:]

        elif vbCodeEnCours == "10":  # numéro de lot taille variable (20max)
            # Si chr(29) ou chr(255), on a atteind la fin du nom de lot (voir norme GS1)
            if vbCutString.find(chr(29)) != -1:  # 124?
                LongNumLot = vbCutString.find(chr(29)) - 2  # chr(124)  ?
                NumLot = vbCutString[2: LongNumLot + 2]
                vbCutString = vbCutString[LongNumLot + 3:]
            elif vbCutString.find(chr(255)) != -1:
                LongNumLot = vbCutString.find(chr(255)) - 3
                NumLot = vbCutString[2: LongNumLot + 2]
                vbCutString = vbCutString[LongNumLot + 4]
            else:
                LongNumLot = len(vbCutString) - 2
                NumLot = vbCutString[2: LongNumLot + 2]
                vbCutString = vbCutString[LongNumLot + 2:]
        elif vbCodeEnCours == "21":  # sérialisation
            # Si chr(29) ou chr(255), on a atteind la fin du n°série (voir norme GS1)
            if vbCutString.find(chr(29)) != -1:  # 124 ?
                LongNumSerialisation = vbCutString.find(chr(29)) - 2  # chr(124) - 2?
                if LongNumSerialisation > 2:
                    NumSer = vbCutString[2: LongNumSerialisation + 2]
                else:
                    NumSer = ""

                vbCutString = vbCutString[-1 * (len(vbCutString) - LongNumSerialisation - 3):]
                # right(vbCutString, vbCutString.Length - LongNumSerialisation - 3)
            elif vbCutString.find(chr(255)) != -1:
                LongNumLot = vbCutString.find(chr(255)) - 3
                vbCutString = vbCutString[LongNumLot + 4:]
            else:
                LongNumLot = len(vbCutString) - 2
                NumSer = vbCutString[2: LongNumLot]
                # le numéro de sérialisaiton peut avoir jusqu'a 20 caractères
                if len(vbCutString) > 22:  # SI supérieur a 2+20 alors y'a autre choses dans le numéro de sérialisation
                    vbCutString = vbCutString[22:]
                else:
                    vbCutString = vbCutString[len(vbCutString):]

        else:
            vbCutString = vbCutString[len(vbCutString) - 1:]

    # if DateExp != "":
    #     # 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.now() + timedelta(days=10)
    #     if DateExp < date_max:
    #         return {
    #             'alertMessage': 'external.scanmed.error.expired',
    #         }, HTTP_200_OK

    try:
        reference = Product.select(Product.designation).where(Product.reference == ref).get()
    except DoesNotExist:
        return {
            'alertMessage': 'external.scanmed.error.unknown.reference',
        }, HTTP_500_INTERNAL_SERVER_ERROR

    current_cip = Cip.select(Cip.qt_boite).where(Cip.cip == CIP13).get() if CIP13 != '' else None

    if CIP13 != '':  # Si boite
        return {
            'reference': ref,
            'reference_designation': reference.designation,
            'batch_number': NumLot,
            'serial': NumSer,
            'fraction': 100,
            'expirationDate': DateExp.strftime('%Y-%m-%d'),
            'cip': CIP13,
            'quantity': current_cip.qt_boite,
            'ucd': (UCD13 != -1 if UCD13 else UCD7),
        }, HTTP_200_OK
    else:  # Si sachet => remonter le contenant

        unit_dose = UnitDose.alias('ud')
        try:
            container = (UnitDose
                         .select(UnitDose.contenant, fn.COUNT(UnitDose.pk).alias('quantity'))
                         .where(UnitDose.contenant ==
                                (unit_dose.select(unit_dose.contenant.distinct()).where(unit_dose.serial == NumSer)))
                         .group_by(UnitDose.contenant)).get()
        except DoesNotExist:
            return {
                'alertMessage': 'external.scanmed.error.unknown.container',
            }, HTTP_500_INTERNAL_SERVER_ERROR

        return {
            'reference': ref,
            'container': container.contenant,
            'reference_designation': reference.designation,
            'batch_number': NumLot,
            'serial': NumSer,
            'fraction': 100,
            'expirationDate': DateExp.strftime('%Y-%m-%d'),
            'cip': CIP13,
            'quantity': container.quantity,
            'ucd': (UCD13 != -1 if UCD13 else UCD7),
        }, HTTP_200_OK
