import logging
import os
from io import StringIO
from typing import Tuple, Optional

from clarabot_nano_common.common.error import log_escape_str
from clarabot_nano_common.common.protocol.request import NanoRequestContext
from clarabot_nano_common.common.protocol.response import NanoBaseResponse

from clarabot_nano.common.request.server_document import DocumentWsReadRequest
from clarabot_nano.server.document.config import DocumentServerConfig
from clarabot_nano.server.document.handler.base import (
    DocumentServerHandleHelper,
    AbstractDocumentSessionNanoServerHandler,
)


class DocumentWsReadResponse(NanoBaseResponse):
    SALT = "salt"
    MESSAGES = "messages"


class DocumentWsReadHandler(AbstractDocumentSessionNanoServerHandler):
    REJECTS_ANONYMOUS_IDENTITY = False  # Further authorization is by room membership and group flags.
    EXECUTION_STATE_CAN_CONFLICT = False  # cacheable and request_key are constant, no conflict is possible
    HANDLER_KEY = DocumentWsReadRequest.TYPE_KEY
    SUCCESS_LOG_LEVEL = logging.DEBUG  # do not log this normally

    def __init__(
        self, helper: DocumentServerHandleHelper, extracted_config: DocumentServerConfig, context: dict, payload: dict
    ):
        AbstractDocumentSessionNanoServerHandler.__init__(self, helper, extracted_config, context, payload)

    async def _create_request_key(self, secure: bool = False) -> Tuple[bool, Optional[list], int]:
        r = (
            False,  # state stream is unique and live for all observers
            [self.HANDLER_KEY, self.session, self.document_session.secret_key],
            2147483647,  # infinite, if reached the client shall just reconnect with a new consumer_id
        )
        if secure:
            return self._secure_request_key(*r)
        return r

    async def _execute_initial(self) -> dict:
        d = await AbstractDocumentSessionNanoServerHandler._execute_initial(self)
        d[DocumentWsReadResponse.SALT] = os.urandom(4)
        d[DocumentWsReadResponse.MESSAGES] = await self.document_session.read(
            0, self.context[NanoRequestContext.PART_SIZE]
        )
        return d

    async def _execute_part(self, part: int, s_hmac_start_part: Optional[int]) -> dict:
        d = await AbstractDocumentSessionNanoServerHandler._execute_part(self, part, s_hmac_start_part)
        d[DocumentWsReadResponse.SALT] = os.urandom(4)
        d[DocumentWsReadResponse.MESSAGES] = await self.document_session.read(
            part, self.context[NanoRequestContext.PART_SIZE]
        )
        return d

    def _log_message(self) -> str:
        return f"Read document session {log_escape_str(self.session)}"

    def build_error_message(self, buf: StringIO) -> None:
        buf.write("Read document session")
        s = self.payload.get(DocumentWsReadRequest.SESSION)
        if s is not None:
            buf.write(" ")
            buf.write(log_escape_str(s))
