import logging
import stat
from typing import Tuple, Optional

import pybase64

from clarabot_nano.common.handler.base import NanoHandlerError
from clarabot_nano.common.protocol.errors import (
    NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST,
    NANO_REQUEST_ERROR_HANDLER_TARGET_NOT_FOUND,
)
from clarabot_nano.common.protocol.response import NanoBaseResponse
from clarabot_nano.common.request.server_general import NanoFilesystemPeekRequest
from clarabot_nano.common.solr.rest import nano_query_get, preparse_solr_response, extract_query_response
from clarabot_nano.indexer.utility import create_docs_query_exact, create_accessor_path
from clarabot_nano.server.base.config import ServerConfig
from clarabot_nano.server.base.handler.base import ServerHandleHelper
from clarabot_nano.server.base.handler.filesystem.base import AbstractFileSystemHandler
from clarabot_nano.server.general.handler.base import GeneralServerHandleHelper
from clarabot_nano.utility.path.validate import ValidationError

logger = logging.getLogger(__name__)


class NanoFilesystemPeekResponse(NanoBaseResponse):
    PEEK = "pk"  # similar data to listing if path exists


class PeekFileSystemHandler(AbstractFileSystemHandler):
    REJECTS_ANONYMOUS_IDENTITY = False  # Further authorization is by room membership and group flags.

    HANDLER_KEY = NanoFilesystemPeekRequest.TYPE_KEY

    helper: GeneralServerHandleHelper  # fix type hinting

    def __init__(self, helper: ServerHandleHelper, extracted_config: ServerConfig, context: dict, payload: dict):
        AbstractFileSystemHandler.__init__(self, helper, extracted_config, context, payload)
        if not isinstance(self.payload.get(NanoFilesystemPeekRequest.PATH), str):
            raise NanoHandlerError(NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST, "Path is missing", self.context)
        self.path = None

    async def _create_request_key(self) -> Tuple[bool, Optional[list], int]:
        return False, None, 1

    async def _execute_initial(self) -> dict:
        try:
            paths, s = self.chroot.paths_stat(self.payload[NanoFilesystemPeekRequest.PATH])
        except (OSError, ValidationError):
            raise NanoHandlerError(NANO_REQUEST_ERROR_HANDLER_TARGET_NOT_FOUND, "Path is invalid", self.context)
        if len(paths) <= 1:  # Disallow peeking the drive-root.
            raise NanoHandlerError(NANO_REQUEST_ERROR_HANDLER_BAD_REQUEST, "Path is invalid", self.context)
        self.path = paths[-1][0]
        # NOTE: we return the name because it *may* be different than in the request path for case-insensitive FS.
        peek = {"name": self.path.name, "mtime": s.st_mtime}
        if stat.S_ISDIR(s.st_mode):
            pass
        else:
            peek["size"] = s.st_size
            parent_accessor_path = create_accessor_path(
                paths[0][0].as_posix(),
                paths[-2][0].relative_to(paths[0][0]).as_posix() if len(paths) > 2 else "",
            )
            if self.extracted_config.solr_query_url:  # Only when Solr is ready.
                async with nano_query_get(
                    self.helper.session,
                    self.extracted_config.solr_query_url,
                    self.extracted_config.solr_auth_header,
                    [
                        ("defType", "lucene"),
                        ("q", create_docs_query_exact(parent_accessor_path, self.path.name)),
                        ("sort", "path asc"),
                        ("fl", "accessors,img_data"),
                        ("start", 0),
                        ("rows", 10),
                    ],
                ) as r:
                    result, success = await preparse_solr_response(r)
                    if success:
                        accessor_path = f"{parent_accessor_path}/{self.path.name}"
                        solr_docs = extract_query_response(result)["docs"]
                        if solr_docs:
                            for solr_doc in solr_docs:
                                if accessor_path in solr_doc["accessors"]:
                                    if img_data := solr_doc.get("img_data"):
                                        peek["img0"] = pybase64.b64decode(img_data)
                                    break
                            else:  # pragma: no cover
                                logger.error(
                                    "Solr returned document with inconsistent accessors for fs-peek: %s %s",
                                    parent_accessor_path,
                                    solr_doc["accessors"],
                                )
                    else:  # pragma: no cover
                        logger.warning("Solr query failed for fs-peek: %s", self.path)
        d = await AbstractFileSystemHandler._execute_initial(self)
        d[NanoFilesystemPeekResponse.PEEK] = peek
        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.context)

    def _log_message(self) -> str:
        return f"Peek {self.path}"
