from io import StringIO
from typing import Tuple, Optional

from clarabot_nano_common.common.handler.error import NanoHandlerError
from clarabot_nano_common.common.protocol.errors import NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST
from clarabot_nano_common.common.protocol.response import NanoBaseResponse

from clarabot_nano.common.request.gateway import AdminCreateSessionRequest
from clarabot_nano.gateway.config import GatewayConfig
from clarabot_nano.gateway.handler.base import AbstractNanoGatewayHandler
from clarabot_nano.gateway.state.admin import AdminAuthenticationError
from clarabot_nano.gateway.state.gateway import GatewayState


class AdminCreateSessionResponse(NanoBaseResponse):
    TOKEN = "token"
    TTL = "ttl"


class AdminCreateSessionHandler(AbstractNanoGatewayHandler):
    REQUIRES_OWNER_IDENTITY = True
    HANDLER_KEY = AdminCreateSessionRequest.TYPE_KEY

    def __init__(self, helper: GatewayState, extracted_config: GatewayConfig, context: dict, payload: dict):
        AbstractNanoGatewayHandler.__init__(self, helper, extracted_config, context, payload)
        self.token: str = None

    async def _create_request_key(self, secure: bool = False) -> Tuple[bool, Optional[list], int]:
        return self._secure_request_key(False, None, 1) if secure else False, None, 1

    async def _execute_initial(self) -> dict:
        try:
            self.token = self.helper.admin_sessions.create_session(
                self.payload.get(AdminCreateSessionRequest.ADMIN_PASSWORD)
            )
        except AdminAuthenticationError as e:
            raise NanoHandlerError(e.code, e.message, self)
        d = await AbstractNanoGatewayHandler._execute_initial(self)
        d[AdminCreateSessionResponse.TOKEN] = self.token
        d[AdminCreateSessionResponse.TTL] = self.helper.admin_sessions.get_token_ttl() - 1
        return d

    async def _execute_part(self, part: int, s_hmac_start_part: Optional[int]) -> dict:  # pragma: no cover
        raise NanoHandlerError(NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST, "Part request is invalid", self)

    def _log_message(self) -> str:
        return f"Create admin-session: {self.token}"

    def build_error_message(self, buf: StringIO) -> None:
        buf.write("Create admin-session")
