import datetime
import json
import logging

from flask import Blueprint, request, session
from flask_jwt_extended import jwt_required
from median.constant import HistoryType, PatientGlobal
from median.models import Adresse, Historique, Stock, Magasin, Product, Gpao
from median.utils import get_counter
from peewee import fn, DoesNotExist
from common.status import HTTP_500_INTERNAL_SERVER_ERROR
from common.util import zero_padding, get_config_global_or_local

logger = logging.getLogger('median')

move_container_blueprint = Blueprint('move_container', __name__)


def _padAddressField(adr):
    if not adr:
        return adr
    _adr_items = adr.split('.')
    _o = []
    for a in _adr_items:
        _o.append(a.rjust(3))
    return '.'.join(_o)


def _moveContainerForward(adr):
    _els = adr.split('.')
    _pos = int(_els[-1].lstrip())
    if _pos > 1:
        return False

    _back_adrs = []
    _els[-1] = '  2'
    _back_adrs.append('.'.join(_els))
    _els[-1] = '  3'
    _back_adrs.append('.'.join(_els))

    _a = (Adresse.select(Adresse.contenant)
          .where((Adresse.adresse == _back_adrs[0]) | (Adresse.adresse == _back_adrs[1])))
    _cont = _a[0].contenant

    (Adresse.update({Adresse.contenant: _cont, Adresse.etat: 'O'})
     .where(Adresse.adresse == adr).execute())

    (Adresse.update({Adresse.contenant: '', Adresse.etat: 'L'})
     .where((Adresse.adresse == _back_adrs[0]) | (Adresse.adresse == _back_adrs[1]))
     .execute())

    (Stock.update({Stock.adresse: adr})
     .where((Stock.adresse == _back_adrs[0]) | (Stock.adresse == _back_adrs[1]))
     .execute())


@move_container_blueprint.route('<string:ref>', methods=['PUT'])
@jwt_required()
def update_container(ref):
    """Move the stock from one adress to another"""
    try:
        args = json.loads(request.data)
    except json.decoder.JSONDecodeError:
        return {'message': 'request.json.data.decode.error'}, HTTP_500_INTERNAL_SERVER_ERROR

    try:
        prod = Product.get(reference=ref)
        logger.info("Move container for product %s" % prod.reference)
    except DoesNotExist:
        return {'message': 'reference.stock.container.reference.not.exists'}, HTTP_500_INTERNAL_SERVER_ERROR

    origin_adresse = _padAddressField(args.get('origin_adresse', ''))
    dest_adresse = _padAddressField(args.get('adresse', ''))
    mag = args.get('magasin', '')  # Magasin d'origine

    lot = args.get('lot', '')
    peremp = args.get('date_peremption', '')
    ucd = args.get('ucd', '')
    fraction = args.get('fraction', '100')
    container = args.get('container', '')
    keep_empty_container = args.get('move_container_is_empty', 'no')

    logger.info('Move container from "%s" to "%s"' % (origin_adresse, dest_adresse))

    # First we need to retrieve the old and the new address
    # If adresses not found, we raise and error
    try:
        mag_from = Magasin.get(Magasin.mag == mag)
        adr_qte_total = (Stock.select(fn.SUM(Stock.quantite)).where(
            Stock.reference == ref, Stock.adresse == origin_adresse)).scalar()
        logger.info("Quantity at address %s is %d" % (origin_adresse, adr_qte_total or 0))
    except DoesNotExist:
        return {'message': 'reference.stock.container.address.from.notexists'}, HTTP_500_INTERNAL_SERVER_ERROR

    try:
        address_to = Adresse.get(Adresse.adresse == dest_adresse)
        mag_dest = Magasin.get(mag=address_to.magasin)
    except DoesNotExist:
        return {'message': 'reference.stock.container.address.to.notexists'}, HTTP_500_INTERNAL_SERVER_ERROR

    qte_tot = (Stock.select(fn.SUM(Stock.quantite)).where(Stock.reference == ref)).scalar()
    logger.info('Compute the total quantity of this product: "%s"' % (qte_tot))

    # we retrieve the container from the from address
    logger.info('Retrieve the old container code from the adresse: "%s"' % (container))

    # mise à jour dans la table f_stock
    logger.debug('Move stock adresse from the old address to the new address...')
    qqq = Stock.update(
        {Stock.adresse: dest_adresse, Stock.magasin: mag}
    ).where(
        (Stock.adresse == origin_adresse)
        & (Stock.reference == ref)
        & (Stock.contenant == container)
    )
    qqq.execute()

    # mise à jour dans la table f_adr
    logger.info('We update the origin address...')
    logger.info('Original address become free...')
    # From address become free
    if keep_empty_container == 'yes':
        # Container is unique, we need to create a new one
        q = Adresse.update({
            Adresse.etat: 'L',
            Adresse.contenant: zero_padding(get_counter("CONTENANT_PASS", 1, True), 9)
        }).where(Adresse.adresse == origin_adresse)
    else:
        q = Adresse.update({Adresse.etat: 'L', Adresse.contenant: ""}).where(Adresse.adresse == origin_adresse)
    q.execute()

    # l'adresse de destination devient occupée
    logger.info('New address become occupied...')
    address_to.etat = "O"
    address_to.contenant = container
    address_to.save()

    # si besoin, on avance les contenants qui se trouvaient derrière
    if keep_empty_container != 'yes':
        check_z: Adresse = Adresse.get_or_none(Adresse.adresse == origin_adresse)
        if check_z and check_z.format == 'BOITE PASS':
            logger.info('If needed, we change position on the pass box')
            _moveContainerForward(origin_adresse)

    logger.info('Save the movement to the history table')
    Historique.create(
        chrono=datetime.datetime.now(),
        reference=ref,
        adresse=dest_adresse,
        adresse_from=origin_adresse,
        quantite_mouvement=0,
        quantite_totale=qte_tot,
        # service=service,
        type_mouvement=HistoryType.Transfert.value,
        lot=lot,
        pmp=0,
        date_peremption=peremp,
        contenant=container,
        poste='MEDIANWEB',
        ucd=ucd,
        magasin=mag_dest.mag,
        fraction=fraction,
        commentaire="Déplacement contenant",
        utilisateur=session['username']
    )
    # Check if the robot between the 2 equipements are the same, if not we create some GPAO movement
    logger.warning("Check if robot code %s == %s" % (mag_from.id_robot, mag_dest.id_robot))
    if mag_from.id_robot != mag_dest.id_robot:
        logger.warning("The robot code are differents, we generate some GPAO movement")
        # Retrieve the k_ua_tranfert parameters
        cfg_poste_from = get_config_global_or_local(mag_from.type_mag, "k_ua_transfert")
        cfg_poste_dest = get_config_global_or_local(mag_dest.type_mag, "k_ua_transfert")

        logger.info("Send GPAO movement to decrease stock from %s" % mag_from.mag)
        Gpao.create(
            poste='MEDIANWEB',
            etat="A",
            ref=ref,
            qte=(-1 * adr_qte_total),
            lot=lot,
            type_mvt="I",
            dest=cfg_poste_from,
            user=session['username'],
            magasin=mag_from.mag,
            tperemp=peremp,
            contenant=container,
            ucd=ucd,
            fraction=fraction,
            sequence=origin_adresse,
            ipp=PatientGlobal.Ipp.value,
            sejour=PatientGlobal.Sejour.value,
            id_zone=mag_from.id_zone,
            id_robot=mag_from.id_robot
        )

        logger.info("Send GPAO movement to increase stock to %s" % mag_dest.mag)
        Gpao.create(
            poste='MEDIANWEB',
            etat="A",
            ref=ref,
            qte=adr_qte_total,
            lot=lot,
            type_mvt="I",
            dest=cfg_poste_dest,
            user=session['username'],
            magasin=mag_dest.mag,
            tperemp=peremp,
            contenant=container,
            ucd=ucd,
            fraction=fraction,
            sequence=dest_adresse,
            ipp=PatientGlobal.Ipp.value,
            sejour=PatientGlobal.Sejour.value,
            id_zone=mag_dest.id_zone,
            id_robot=mag_dest.id_robot
        )

        logger.info("Send GPAO movement to increase stock to %s" % mag_dest.mag)

    logger.info('End to move the container')

    return "ok"
