import json
import logging
from datetime import datetime

from flask import Blueprint, request, session
from flask_jwt_extended import jwt_required

from median.models import Cip, User, Gtin, Ucd, Product
from ressources.blueprint.config.config_service import DrugStandard, get_drug_standard
from median.database import mysql_db
from median.views import RawConfig
from median.constant import MEDIANWEB_POSTE
from common.models import WebLogActions

from common.status import (
    HTTP_500_INTERNAL_SERVER_ERROR,
    HTTP_204_NO_CONTENT,
    HTTP_403_FORBIDDEN,
    HTTP_200_OK,
    HTTP_400_BAD_REQUEST,
)

cip_blueprint = Blueprint("cip", __name__)

auth_profils = ["ECO-DEX", "REFERENT", "PHARMACIEN", "ADMIN"]

logger = logging.getLogger("median")


def file_to_base64(cip):
    encoded_string = None
    return encoded_string


@cip_blueprint.route("reference/<string:ref_pk>", methods=["GET"])
@jwt_required()
def get_by_reference(ref_pk):
    try:
        cips = (
            Gtin.select(Gtin.cip)
            .join(Ucd, on=Gtin.ucd == Ucd.ucd)
            .join(Product, on=Product.reference == Ucd.reference)
            .where(Product.pk == ref_pk)
            .group_by(Gtin.cip)
        )

        logger.debug("Lines : %s." % len(cips))

        return {
            "data": [
                {
                    "cip": s.cip,
                }
                for s in cips
            ]
        }, HTTP_200_OK

    except Exception as error:
        logger.error("Get Ucd raised an exception: ", error.args)
        return {"message": error.args}, HTTP_500_INTERNAL_SERVER_ERROR


@cip_blueprint.route("<string:cip_pk>", methods=["PUT"])
@jwt_required()
def update_cip(cip_pk):
    try:
        profil = User.select(User.profil).where((User.pk == session["user_id"]) & User.profil << auth_profils).count()
        cfg_cip_add = RawConfig(MEDIANWEB_POSTE).read("k_eco_cip_index_libre")
        can_add_index_libre = cfg_cip_add and cfg_cip_add.value == "1"

        if profil == 1:
            with mysql_db.atomic() as transaction:
                try:
                    data = json.loads(request.data)
                    try:
                        qty = int(data["qtePass"]) if "qtePass" in data else None
                        qtyBoite = int(data["qteBoite"]) if "qteBoite" in data else None
                        qtyBlister = int(data["qteBlister"]) if "qteBlister" in data else None
                        index = int(data["index"]) if "index" in data else None
                    except Exception as parse_error:
                        logger.error("Invalid data in update_cip: %s", parse_error)
                        return {"message": "ucd_cip.error.invalid_data"}, HTTP_400_BAD_REQUEST

                    cip: Cip = Cip.select().where(Cip.pk == cip_pk).get()

                    check_existing_cip = Cip.get_or_none(
                        (Cip.cip == cip.cip) & (Cip.ucd == cip.ucd) & (Cip.dossier == index)
                    )

                    if check_existing_cip and check_existing_cip.pk != cip.pk:
                        return {"message": "ucd_cip.error.cip.exist"}, HTTP_500_INTERNAL_SERVER_ERROR

                    # check if the index will be changed
                    index_has_changed = cip.dossier != index

                    if int(cip.dossier) == 0 or can_add_index_libre:
                        cip.qt_pass = qty
                        cip.qt_boite = qtyBoite
                        cip.qt_blister = qtyBlister
                        cip.dossier = index if can_add_index_libre else cip.dossier
                        cip.save()

                        log_message = f"Updated ucd/cip : {cip.ucd} / {cip.cip} with index {cip.dossier}"
                        if index_has_changed:
                            log_message += f" (changed from {cip.dossier} to {index})"
                        else:
                            log_message += " (no change in index)"
                        log_cip(session["username"], "update", log_message)

                        return {}, HTTP_204_NO_CONTENT
                    else:
                        return {
                            "message": "ucd_cip.error.index0" if cip.dossier == "0" else "ucd_cip.error.qty.notnumeric"
                        }, HTTP_500_INTERNAL_SERVER_ERROR
                except Exception as error:
                    transaction.rollback()
                    logger.error("Update Cip raised an exception: ", error.args)
                    return {"message": error.args}, HTTP_500_INTERNAL_SERVER_ERROR
        else:
            return {"message": "ucd_cip.error.profil"}, HTTP_403_FORBIDDEN
    except Exception as error:
        logger.error("Update Cip raised an exception: ", error.args)
        return {"message": "ucd_cip.error.generic", "error": error.args}, HTTP_500_INTERNAL_SERVER_ERROR


@cip_blueprint.route("", methods=["POST"])
@jwt_required()
def create_cip():
    try:
        profil = User.select(User.profil).where((User.pk == session["user_id"]) & User.profil << auth_profils)
        cfg_cip_add = RawConfig(MEDIANWEB_POSTE).read("k_eco_cip_index_libre")
        can_add_index_libre = cfg_cip_add and cfg_cip_add.value == "1"

        if any(profil):
            data = json.loads(request.data)
            cip_data = data["cip"]
            ucd = data["ucd"]

            if not cip_data["cip"].isdigit():
                drugs_standard = get_drug_standard()
                return {"message": f"threshold.{drugs_standard.value}.error.digit"}, HTTP_400_BAD_REQUEST

            if not _check_lengths(cip_data["cip"]):
                drugs_standard = get_drug_standard()
                return {"message": f"threshold.{drugs_standard.value}.error.length"}, HTTP_400_BAD_REQUEST

            if not can_add_index_libre and int(cip_data["index"]) > 0:
                return {"message": "ucd_cip.create.error.dossier"}, HTTP_500_INTERNAL_SERVER_ERROR

            gtins = Gtin.select(Gtin.pk).where((Gtin.ucd == ucd) & (Gtin.cip == cip_data["cip"]))

            if not any(gtins):
                gtin = Gtin()
                gtin.dossier = cip_data["index"]
                gtin.ucd = ucd
                gtin.cip = cip_data["cip"]
                gtin.qt_blister = cip_data["qte_blister"]
                gtin.qt_pass = cip_data["qtePass"]
                gtin.qt_boite = cip_data["qte_boite"]
                gtin.save()

                log_cip(
                    session["username"],
                    "add",
                    f"Added new ucd/cip : {gtin.ucd} / {gtin.cip} with index {gtin.dossier} ",
                )

                return {}, HTTP_204_NO_CONTENT
            else:
                return {"message": "ucd_cip.create.error.cip.exist"}, HTTP_500_INTERNAL_SERVER_ERROR
        else:
            return {"message": "ucd_cip.error.profil"}, HTTP_403_FORBIDDEN
    except Exception as error:
        logger.error("Create Cip raised an exception: ", error.args)
        return {"message": "ucd_cip.error.generic", "error": error.args}, HTTP_500_INTERNAL_SERVER_ERROR


@cip_blueprint.route("<string:ucd>", methods=["GET"])
@jwt_required()
def get(ucd):
    try:
        filtered_cip = (
            Cip.select(Cip.pk, Cip.ucd, Cip.cip, Cip.qt_pass, Cip.qt_boite, Cip.qt_blister, Cip.dossier).where(
                Cip.ucd == ucd
            )
        ).order_by(Cip.ucd, Cip.cip, Cip.dossier.desc())

        logger.debug("Lines : %s." % len(filtered_cip))

        return {
            "data": [
                {
                    "image": file_to_base64(s),
                    "pk": s.pk,
                    "ucd": s.ucd,
                    "cip": s.cip,
                    "qte_pass": s.qt_pass,
                    "qte_boite": s.qt_boite,
                    "qte_blister": s.qt_blister,
                    "index": s.dossier,
                }
                for s in filtered_cip
            ]
        }, HTTP_200_OK

    except Exception as error:
        logger.error("Get Ucd raised an exception: ", error.args)
        return {"message": error.args}, HTTP_500_INTERNAL_SERVER_ERROR


def _check_lengths(value):
    drugs_standard = get_drug_standard()
    valid_lengths = {DrugStandard.Pzn: [7, 8], DrugStandard.Cnk: [7, 13]}.get(drugs_standard, [7, 13])
    return len(value) in valid_lengths


@cip_blueprint.route("config", methods=["GET"])
@jwt_required()
def get_config():
    try:
        cfg_cip_add = RawConfig(MEDIANWEB_POSTE).read("k_eco_cip_index_libre")
        if cfg_cip_add:
            can_add_index_libre = cfg_cip_add.value == "1"
        else:
            can_add_index_libre = False

        return {"value": can_add_index_libre == 1}, HTTP_200_OK
    except Exception:
        return 0


def log_cip(username: str, action: str, message: str):
    """
    Add new log for the cip blueprint (Cip)

    :param username: User made the action to log
    :param action:
    :param message: message to log
    """
    logger.info("CIP[%s](%s)): %s" % (action, username, message))
    wlog = WebLogActions()
    wlog.chrono = datetime.now()
    wlog.username = username
    wlog.equipement_type = ""
    wlog.action = action
    wlog.message = message
    wlog.save()
