import json
import logging
import operator
import os
import uuid
from functools import reduce

from flask import Blueprint, request, send_file
from flask_jwt_extended import jwt_required
from median.models import Prescription, PrescriptionItem, Product, Patient, Sejour, \
    Service
from peewee import DoesNotExist, JOIN
from ressources.astus.utils import generate_excel_file

prescriptions_blueprint = Blueprint('prescriptions', __name__)

logger = logging.getLogger('median')


def get_all_prescriptions(args):
    v_search = args['search'] if ('search' in args) else None
    logger.info("Récupérer les emplacements")

    if v_search is None:
        v_search = ''

    andexpr = True

    if len(v_search) > 0:
        fullname = Patient.prenom + ' ' + Patient.nom
        lst = list(map(lambda s: (
            (fullname.contains(s.strip())) |
            (Prescription.ipp.contains(s.strip())) |
            (Service.libelle.contains(s.strip()))
        ), v_search))
        search = reduce(operator.and_, lst)

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

    total_prescriptions_count = Prescription \
        .select(Prescription.ordre, Prescription.ipp, Patient.prenom, Patient.nom,
                PrescriptionItem.sequence, PrescriptionItem.sub_sequence,
                PrescriptionItem.reference, Product.designation,
                PrescriptionItem.posologie,
                Sejour.date_entree, Sejour.date_sortie,
                Service.libelle) \
        .join(Patient, JOIN.LEFT_OUTER, on=(Prescription.ipp == Patient.ipp)) \
        .switch(Prescription) \
        .join(Sejour, JOIN.LEFT_OUTER, on=((Sejour.sejour == Prescription.sejour) & (Sejour.ipp == Prescription.ipp))) \
        .switch(Prescription) \
        .join(PrescriptionItem, JOIN.LEFT_OUTER, on=(Prescription.ordre == PrescriptionItem.ordre)) \
        .join(Product, JOIN.LEFT_OUTER, on=(Product.reference == PrescriptionItem.reference)) \
        .switch(Prescription) \
        .join(Service, JOIN.LEFT_OUTER, on=(Service.code == Sejour.uf_hosp)) \
        .where(expr) \
        .order_by(Service.libelle, Prescription.ordre, Prescription.ipp)

    return total_prescriptions_count


def get_obj_prescriptions(i):
    return {
        'ordre': i.ordre,
        'ipp': i.ipp,
        'patientFirstName': i.patient.prenom if hasattr(i, 'patient') else i.prenom,
        'patientName': i.patient.nom if hasattr(i, 'patient') else i.nom,
        'reference': i.reference,
        'designation': i.product.designation,
        'posology': i.prescriptionitem.posologie if hasattr(i, 'prescriptionitem') else None,
        'ward': i.service.libelle if hasattr(i, 'service') else None,

    }


def get_obj_prescription(i):
    return {
        'ordre': i.ordre,
        'ipp': i.ipp,
        'patientFirstName': i.patient.prenom if hasattr(i, 'patient') else i.prenom,
        'patientName': i.patient.nom if hasattr(i, 'patient') else i.nom,
        'ward': i.service.libelle if hasattr(i, 'service') else None,

    }


def get_obj_prescription_item(i):
    if hasattr(i, 'prescriptionitem'):
        return {
            'reference': i.prescriptionitem.reference,
            'designation': i.prescriptionitem.product.designation,
            'posology': i.prescriptionitem.posologie,
            'sequence': i.prescriptionitem.sequence,
            'subsequence': i.prescriptionitem.sub_sequence

        }
    else:
        return None


@prescriptions_blueprint.route('', methods=['POST'])
@jwt_required()
def get_locations():
    try:
        args = json.loads(request.data)
        v_start = args['start']
        v_length = args['length']
        total_prescriptions_count = get_all_prescriptions(args)
        paged_emplacements = total_prescriptions_count.limit(v_length).offset(v_start)

        list_prescription = []

        for presc in paged_emplacements:
            list_res = list(filter(lambda h: (h['ordre'] == presc.ordre), list(list_prescription)))
            item = get_obj_prescription_item(presc)

            if not any(list_res):
                obj = get_obj_prescription(presc)
                obj['items'] = []
                list_prescription.append(obj)
            else:
                obj = list_res[0]

            if item is not None:
                obj['items'].append(item)

        return {
            'data': list_prescription,
            'recordsTotal': total_prescriptions_count.count(),
        }

    except DoesNotExist:
        logger.error('Get reappro items Datatables raised a DoesNotExist exception')
        return {
            'data': [],
            'recordsTotal': 0,
        }
    except AttributeError as e:
        logger.error(('get all prescription raised an exception: ', e.args))
        return {
            'data': [],
            'recordsTotal': 0,
            'recordsFiltered': 0,
            'error': 'filterByMagasin missing from the request'
        }
    except Exception as error:
        logger.error(('Datatables emplacements raised an exception: ', error.args))
        return {
            'data': [],
            'recordsTotal': 0,
            'error': error.args
        }


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

    name = os.sep.join(
        [os.getcwd(), "tmp_export", "%s.xlsx" % uuid.uuid4()])
    stocks = get_all_prescriptions(data)
    generate_excel_file(name, headers, stocks, get_obj_prescriptions())

    return send_file(name, as_attachment=True)
