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

from clarabot_nano_common.common.error import maybe_rad32, log_escape_str
from clarabot_nano_common.common.handler.error import NanoHandlerError
from clarabot_nano_common.common.protocol.errors import NANO_REQUEST_ERROR_HANDLER_TARGET_NOT_FOUND
from clarabot_nano_common.common.protocol.request import NanoRequestContext
from clarabot_nano_common.common.protocol.response import NanoBaseResponse
from clarabot_nano_common.utility.algorithm import dict_nested_get

from clarabot_nano.common.request.gateway import DescribeSpecificInterNanoJobRequest
from clarabot_nano.gateway.config import GatewayConfig
from clarabot_nano.gateway.handler.inter_nano_job.base import AbstractSpecificInterNanoJobHandler
from clarabot_nano.gateway.state.gateway import GatewayState
from clarabot_nano.inter.job import AbstractInterNanoJobQueue


class DescribeSpecificInterNanoJobResponse(NanoBaseResponse):
    JOB = "job"


class DescribeSpecificInterNanoJobHandler(AbstractSpecificInterNanoJobHandler):
    EXECUTION_STATE_CAN_CONFLICT = False  # cacheable and request_key are constant, no conflict is possible
    SUCCESS_LOG_LEVEL = logging.DEBUG  # do not log this normally to avoid spam

    HANDLER_KEY = DescribeSpecificInterNanoJobRequest.TYPE_KEY

    def __init__(self, helper: GatewayState, extracted_config: GatewayConfig, context: dict, payload: dict):
        AbstractSpecificInterNanoJobHandler.__init__(self, helper, extracted_config, context, payload)
        # these will be saved for logging
        self.room_id = 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:
        self.room_id = dict_nested_get(self.context, NanoRequestContext.ROOM, NanoRequestContext.ROOM_ID)
        ijq: AbstractInterNanoJobQueue = self.helper.inter_job_queue
        job = await ijq.get(self.job_id)
        if not job or job.room_id_int64 != self.room_id:
            raise NanoHandlerError(NANO_REQUEST_ERROR_HANDLER_TARGET_NOT_FOUND, "Unknown job", self)
        d = await AbstractSpecificInterNanoJobHandler._execute_initial(self)
        d[DescribeSpecificInterNanoJobResponse.JOB] = await self.helper.inter_job_queue.describe(job)
        return d

    def _log_message(self) -> str:
        return f"Describe inter-nano job {self.job_id} for room {maybe_rad32(self.room_id)}"

    def build_error_message(self, buf: StringIO) -> None:
        buf.write("Describe inter-nano job ")
        buf.write(log_escape_str(self.payload.get(DescribeSpecificInterNanoJobRequest.JOB_ID)))
        buf.write(" for room ")
        try:
            room_id = dict_nested_get(self.context, NanoRequestContext.ROOM, NanoRequestContext.ROOM_ID)
        except KeyError:
            pass
        else:
            buf.write(maybe_rad32(room_id))
