import json
import logging
import time

from typing import (
    List,
    Optional,
)

from bustoken import (
    TK_NDISCOUNT_ADD_BENEFIT,
    TK_NDISCOUNT_GET_BENEFITS,
    TK_NDISCOUNT_REMOVE,
)
from msgbus import (
    FM_PARAM,
    MBMessage,
    TK_SYS_ACK,
)
from sysactions import send_message

from model.customexception import (
    DiscountMinLevelException,
    ErrorApplyingDiscount,
    ErrorGettingBenefits,
)


class NDiscountInteractor(object):
    def __init__(self):
        # type: () -> None
        self.logger = logging.getLogger("PosActions")

    def get_benefits(
        self,           # type: NDiscountInteractor
        pos_id,         # type: int
        only_visible,   # type: bool
    ):
        # type: (...) -> json

        self.logger.info("Getting benefits for POS: {}".format(pos_id))
        retry = 0
        while retry < 3:
            time.sleep(0.5 * retry)
            try:
                data = json.dumps(dict(posId=pos_id, only_visible=only_visible))
                response = send_message(
                    dest_name="nDiscount",
                    token=TK_NDISCOUNT_GET_BENEFITS,
                    format=FM_PARAM,
                    data=data,
                    timeout=30000000,
                )
                if response.token == TK_SYS_ACK:
                    return json.loads(response.data)
            except Exception as ex:
                self.logger.warn("Error getting benefits data: {} - Retry attempt: {}".format(ex.message, retry))

            retry += 1

        self.logger.exception("[get_benefits] benefits retry failed")
        raise ErrorGettingBenefits()

    @staticmethod
    def add_benefit(
            pos_id,                     # type: str
            benefit_id,                 # type: str
            benefit,                    # type: json
            user_auth_level=False       # type: Optional[int]
    ):
        # type: (...) -> None

        data = json.dumps(dict(
                posId=pos_id,
                benefitId=benefit_id,
                benefit=benefit,
                user_auth_level=user_auth_level
        ))

        response = send_message("nDiscount", TK_NDISCOUNT_ADD_BENEFIT, FM_PARAM, data, timeout=30000000)
        if response.token != TK_SYS_ACK:
            error_code = response.data.split("\0")[0]
            if error_code == "NDISCOUNT_ERROR_USER_MIN_LEVEL":
                raise DiscountMinLevelException(min_level_required=int(response.data.split("\0")[1]))

            raise ErrorApplyingDiscount(error_code=response.data)

    @staticmethod
    def remove_benefits(pos_id, benefit_ids=None):
        # type: (str, List) -> MBMessage

        data = json.dumps(dict({"pos_id": pos_id, "benefit": benefit_ids}))
        return send_message("nDiscount", TK_NDISCOUNT_REMOVE, FM_PARAM, data, timeout=30000000)

    @staticmethod
    def remove_benefit(pos_id, benefit_id):
        # type: (str, int) -> MBMessage

        data = json.dumps(dict({"pos_id": pos_id, "benefit": benefit_id}))
        return send_message("nDiscount", TK_NDISCOUNT_REMOVE, FM_PARAM, data, timeout=30000000)

    @staticmethod
    def test_connection():
        # type: () -> bool
        try:
            data = json.dumps(dict())
            response = send_message("nDiscount", TK_NDISCOUNT_GET_BENEFITS, FM_PARAM, data, timeout=30000000)
            if response:
                return True
            return False
        except Exception as _:
            return False
