import json
from typing import (
    Any,
    Dict,
    List,
    Optional,
)

from messagebus import (
    DataType,
    DefaultToken,
    Message,
    MessageBus,
)

from .dto import SaleChallenge
from .shared.token import (
    TK_DAILY_GOALS_DELETE_SALES_CHALLENGE,
    TK_DAILY_GOALS_GET_SALES_CHALLENGE,
    TK_DAILY_GOALS_INSERT_SALES_CHALLENGE,
    TK_DAILY_GOALS_UPDATE_SALES_CHALLENGE,
)

COMPONENT_NAME = "DailyGoals"


class DailyGoalsApiError(Exception):
    pass


class DailyGoalsService(object):

    def __init__(
        self,           # type: DailyGoalsService
        message_bus,    # type: MessageBus
    ):
        # type: (...) -> None

        self.message_bus = message_bus

    def insert_sales_challenge(
        self,           # type: DailyGoalsService
        name,           # type: str
        start_date,     # type: str
        end_date,       # type: str
        products,       # type: List[int]
        created_by,     # type: int
    ):
        # type: (...) -> int

        challenge = SaleChallenge(
            name=name,
            start_date=start_date,
            end_date=end_date,
            products=products,
            created_by=created_by,
        )
        reply = self._send(
            token=TK_DAILY_GOALS_INSERT_SALES_CHALLENGE,
            request=challenge.to_json(),
        )

        return int(json.loads(reply.data.decode("utf-8")))

    def get_sales_challenge(
        self,               # type: DailyGoalsService
        limit=None,         # type: Optional[int]
        challenge_id=None,  # type: Optional[int]
    ):
        # type: (...) -> List[SaleChallenge]

        request = {
            "limit": limit,
            "id": challenge_id,
        }
        reply = self._send(
            token=TK_DAILY_GOALS_GET_SALES_CHALLENGE,
            request=request,
        )
        challenges = json.loads(reply.data.decode("utf-8"))

        return [SaleChallenge.from_json(data=challenge) for challenge in challenges]

    def update_sales_challenge(
        self,           # type: DailyGoalsService
        challenge_id,   # type: int
        name,           # type: str
        start_date,     # type: str
        end_date,       # type: str
        products,       # type: List[int]
        created_by,     # type: int
        operator_id,    # type: int
    ):
        # type: (...) -> int

        challenge = SaleChallenge(
            name=name,
            start_date=start_date,
            end_date=end_date,
            products=products,
            created_by=created_by,
            challenge_id=challenge_id,
        )
        request = challenge.to_json()
        request["operator_id"] = operator_id
        reply = self._send(
            token=TK_DAILY_GOALS_UPDATE_SALES_CHALLENGE,
            request=request,
        )

        return int(json.loads(reply.data.decode("utf-8")))

    def delete_sales_challenge(
        self,           # type: DailyGoalsService
        challenge_id,   # type: int
        operator_id,    # type: int
    ):
        # type: (...) -> int

        request = {
            "sale_challenge_id": challenge_id,
            "operator_id": operator_id,
        }
        reply = self._send(
            token=TK_DAILY_GOALS_DELETE_SALES_CHALLENGE,
            request=request,
        )

        return int(json.loads(reply.data.decode("utf-8")))

    def _send(
        self,       # type: DailyGoalsService
        token,      # type: int
        request,    # type: Dict[str, Any]
    ):
        # type: (...) -> Message

        reply = self.message_bus.send_message(
            component=COMPONENT_NAME,
            message=Message(
                token=token,
                data_type=DataType.json,
                data=json.dumps(request).encode("utf-8"),
            ),
        )
        if reply.token != DefaultToken.TK_SYS_ACK:
            raise DailyGoalsApiError("DailyGoals request with token {0} failed".format(token))

        return reply
