from typing import Optional

import wx
from clarabot_nano_common.ui.utility import Slots
from clarabot_nano_common.utility.serialize import msgpack_dumps

from clarabot_nano.common.auth.two_factor import AbstractTwoFactorErrorHandler, TwoFactorError
from clarabot_nano.control.client import WxCoreInterface
from clarabot_nano.control.common import gateway_command_result


class TwoFactorGatewayRequestMixin(AbstractTwoFactorErrorHandler):
    def __init__(self, core_interface: WxCoreInterface):
        AbstractTwoFactorErrorHandler.__init__(self)
        assert isinstance(self, wx.Window)
        assert isinstance(self, Slots)
        self.core_interface = core_interface
        self._active_request_type = None
        self._active_request = {}
        self._followups = 0

        self.bind_signal(self.core_interface.gateway_command_result, self._on_gateway_command_result)

    def _start_gateway_request(self, request_type: bytes, req_args: dict, feedback_text: str):
        self._active_request_type = request_type
        self._active_request = {}
        self._followups = 0
        self._update_request(req_args)
        self._enable_ui(False)
        self._set_feedback_text(feedback_text)

    def _set_feedback_text(self, text: str):
        raise NotImplementedError()

    def _enable_ui(self, b: bool):
        raise NotImplementedError()

    def _update_request(self, new_args: dict):
        self._active_request.update(new_args)
        self.core_interface.gateway_request([self._active_request_type, msgpack_dumps(self._active_request)])

    def _on_gateway_command_result(self, response_received, result):
        self._followups += 1
        if self._followups > 10:  # pragma: no cover
            self._set_feedback_text(gettext("Maximum follow-up requests reached."))
        elif response_received:
            if tfa := self.get_two_factor(result):
                try:
                    extra_args = self.handle_two_factor_response(*tfa)
                except TwoFactorError as e:
                    self._set_feedback_text(e.message)
                else:
                    self._update_request(extra_args)
                    return  # do not re-enable the UI
            else:
                self._set_feedback_text(gateway_command_result(result, False))
        else:
            self._set_feedback_text(gateway_command_result(result, True))
        self._enable_ui(True)
        self._gateway_request_ended(response_received, result)
        self._active_request = {}  # free

    def _gateway_request_ended(self, response_received, result):
        pass

    def _query_user_2fa_code(self) -> Optional[str]:
        with wx.TextEntryDialog(
            self,
            gettext("Two-factor authentication code is required for the request to be executed."),
            gettext("Enter 2FA code"),
        ) as d:
            if d.ShowModal() == wx.ID_OK:
                # Accidental space/newline at end from clipboard-copy may confuse users, so strip those.
                return d.GetValue().strip()
