import base64
import socket

import safe_radix32
import wx
import wx.adv
from clarabot_nano_common.utility.serialize import msgpack_dumps

from clarabot_nano import __version__
from clarabot_nano.common.core.message import MSG_TYPE_REQUEST_CONFIG_UPDATE
from clarabot_nano.common.crypto.admin_password import ADMIN_PASSWORD_CONTEXT
from clarabot_nano.control.about import AboutDialog
from clarabot_nano.control.configure.basic.wizard import SimpleConfigurationWizard
from clarabot_nano.control.configure.drive.attach import DriveAttachDialog
from clarabot_nano.control.configure.drive.manager import DriveConfigManager
from clarabot_nano.control.nano.config import ConfigUpdateProgressDialog
from clarabot_nano.control.nano.reset import ResetProgressDialog
from clarabot_nano.control.page.status import ControlStatus
from clarabot_nano.control.window import ControlWindow
from clarabot_nano.development.spawn.control.tester import ControlApplicationTester, phase_timeout


class _InitLoginControlApplicationTester(ControlApplicationTester):
    """
    Test initial logging in with Nano.
    """

    def __init__(self, *args, email=None, password=None, tmp_dir=None, **kwargs):
        ControlApplicationTester.__init__(self, *args, **kwargs)
        self.email = email
        self.password = password
        self.tmp_dir = tmp_dir

        self.test_block_data = {"id": safe_radix32.encode_fw(8888888), "block_names": ["block-x"]}

    @phase_timeout(5.0)
    def _phase_0(self, w: ControlWindow):
        """
        When the nano is not logged in (gateway_status is wait-config) the window will be shown on startup, even if
        tray icon is available. The central widget will be the ControlLogin widget.
        """
        self._check_waiting_login_get_central_widget(w)
        return True

    def _phase_1(self, w: ControlWindow):
        """
        Assert login form state-consistency.
        """
        login_widget = self._check_waiting_login_get_central_widget(w)
        assert not login_widget._command_result_label.GetLabelText()

        assert not login_widget._email_field.GetLineText(0)
        assert not login_widget._password_field.GetLineText(0)
        assert not login_widget._login_btn.IsEnabled()

        login_widget._email_field.SetValue("tmp@who.xx")
        assert login_widget._email_field.GetLineText(0)
        assert not login_widget._password_field.GetLineText(0)
        assert not login_widget._login_btn.IsEnabled()

        login_widget._password_field.SetValue("asdf")
        assert login_widget._email_field.GetLineText(0)
        assert login_widget._password_field.GetLineText(0)
        assert login_widget._login_btn.IsEnabled()

        login_widget._email_field.SetValue("")
        assert not login_widget._email_field.GetLineText(0)
        assert login_widget._password_field.GetLineText(0)
        assert not login_widget._login_btn.IsEnabled()

        assert not login_widget._command_result_label.GetLabelText()

        return True

    def _phase_2(self, w: ControlWindow):
        """
        Submit invalid login credentials.
        """
        login_widget = self._check_waiting_login_get_central_widget(w)

        login_widget._email_field.SetValue(self.email)
        login_widget._password_field.SetValue("whatapassword")
        assert not login_widget._command_result_label.GetLabelText()
        login_widget.ProcessEvent(wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, login_widget._login_btn.GetId()))
        assert login_widget._command_result_label.GetLabelText()
        assert w._inhibits_sleep is False

        return True

    @phase_timeout(5.0)
    def _phase_3(self, w: ControlWindow):
        """
        Wait login failure.
        """
        login_widget = self._check_waiting_login_get_central_widget(w)

        assert login_widget._email_field.GetLineText(0) == self.email
        assert not login_widget._password_field.GetLineText(0)
        assert not login_widget._login_btn.IsEnabled()
        assert w._inhibits_sleep is False

        assert login_widget._command_result_label.GetLabelText() == gettext("Login failed.")

        return True

    def _phase_4(self, w: ControlWindow):
        """
        Submit correct login credentials.
        """
        login_widget = self._check_waiting_login_get_central_widget(w)

        login_widget._password_field.SetValue(self.password)
        login_widget.ProcessEvent(wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, login_widget._login_btn.GetId()))

        return True

    @phase_timeout(5.0)
    def _phase_5(self, w: ControlWindow):
        """
        Check successful login effects.
        """
        config = w.core_interface.get_config()
        assert (v := config["state"].get("gateway_status")) == "connected", str(v)
        assert w.IsShown()
        central_widget = w.central_widget()
        assert isinstance(central_widget, ControlStatus)
        assert w._inhibits_sleep is True
        # TODO: central_widget._gateway_status_label.text

        return True

    @phase_timeout(5.0)
    def _phase_6(self, w: ControlWindow):
        """
        Check if the initial config dialog is started
        """
        d = self._get_modal_dialog(w, wx.adv.Wizard)
        assert isinstance(d, SimpleConfigurationWizard)
        return True

    @phase_timeout(25.0)
    def _phase_7(self, w: ControlWindow):
        """
        Check successful nano license activation.
        """
        config = w.core_interface.get_config()
        assert (v := config["state"].get("gateway_status")) == "active", str(v)
        assert w.IsShown()
        central_widget = w.central_widget()
        assert isinstance(central_widget, ControlStatus)
        assert w._inhibits_sleep is True
        # TODO: central_widget._gateway_status_label.text

        return True

    def _phase_8(self, w: ControlWindow):
        """
        Advance the setup wizard.
        """
        d = self._get_modal_dialog(w, wx.adv.Wizard)
        assert isinstance(d, SimpleConfigurationWizard)
        assert d.GetCurrentPage() is d.intro_page
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        return True

    def _phase_9(self, w: ControlWindow):
        """
        Change name.
        """
        d = self._get_modal_dialog(w, wx.adv.Wizard)
        assert isinstance(d, SimpleConfigurationWizard)
        assert d.GetCurrentPage() is d.name_page
        assert d.name_page.name.GetValue() == socket.gethostname()
        d.name_page.name.SetValue("")
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        assert d.GetCurrentPage() is d.name_page
        d.name_page.name.SetValue("test-login-nano")
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        return True

    def _phase_10(self, w: ControlWindow):
        """
        Set password.
        """
        d = self._get_modal_dialog(w, wx.adv.Wizard)
        assert isinstance(d, SimpleConfigurationWizard)
        assert d.GetCurrentPage() is d.password_page
        assert not d.password_page.pw.GetValue()
        assert not d.password_page.pw_check.GetValue()
        d.password_page.pw.SetValue("tpass")
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        assert d.GetCurrentPage() is d.password_page
        d.password_page.pw_check.SetValue("xpass")
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        assert d.GetCurrentPage() is d.password_page
        d.password_page.pw_check.SetValue("tpass")
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        return True

    def _phase_11(self, w: ControlWindow):
        """
        Set indexed languages.
        """
        d = self._get_modal_dialog(w, wx.adv.Wizard)
        assert isinstance(d, SimpleConfigurationWizard)
        assert d.GetCurrentPage() is d.index_language_page
        assert d.index_language_page.radio_list.GetValue()
        d.index_language_page.radio_all.SetValue(True)
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        return True

    def _phase_12(self, w: ControlWindow):
        """
        Set index ignored mimes.
        """
        d = self._get_modal_dialog(w, wx.adv.Wizard)
        assert isinstance(d, SimpleConfigurationWizard)
        assert d.GetCurrentPage() is d.index_ignore_mimes_page
        s = d.index_ignore_mimes_page.mimes.GetStrings()
        s.append("test/me")
        d.index_ignore_mimes_page.mimes.SetStrings(s)
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        return True

    def _phase_13(self, w: ControlWindow):
        """
        Apply configs.
        """
        d = self._get_modal_dialog(w, wx.adv.Wizard)
        assert isinstance(d, SimpleConfigurationWizard)
        assert d.GetCurrentPage() is d.finish_page
        self._find_send_command_event(d, wx.ID_FORWARD, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        return True

    @phase_timeout(5.0)
    def _phase_14(self, w: ControlWindow):
        """
        Wait until apply finishes.
        """
        upd_dialog = self._get_modal_dialog(w, ConfigUpdateProgressDialog)
        message_dialog = self._get_modal_dialog(upd_dialog, wx.GenericMessageDialog)
        assert message_dialog.GetMessage() == gettext("Configurations updated successfully!")
        message_dialog.EndModal(wx.ID_OK)
        return True

    @phase_timeout(5.0)
    def _phase_15(self, w: ControlWindow):
        """
        Ack done and updated config.
        """
        upd_dialog = self._get_modal_dialog(w, ConfigUpdateProgressDialog, require=False)
        assert not upd_dialog
        config = w.core_interface.get_config()
        assert (v := config["remote"].get("name")) == "test-login-nano", str(v)
        try:
            ADMIN_PASSWORD_CONTEXT.verify("tpass", config["remote"].get("admin_password"))
        except (TypeError, ValueError) as e:
            assert False, str(e)
        assert (v := config["remote"].get("indexed_languages")) == "all", str(v)
        v = config["remote"].get("indexer_ignored_mimes")
        assert v and len(v) > 1 and "test/me" in v, str(v)
        return True

    def _phase_16(self, w: ControlWindow):
        """
        Start reset.
        """
        assert w._inhibits_sleep is True
        w.AddPendingEvent(wx.CommandEvent(wx.wxEVT_COMMAND_MENU_SELECTED, w._reset_item.GetId()))
        return True

    @phase_timeout(5.0)
    def _phase_17(self, w: ControlWindow):
        """
        Confirm reset.
        """
        reset_dialog = self._get_modal_dialog(w, wx.GenericMessageDialog)
        assert reset_dialog.GetTitle() == gettext("Confirm reset")
        reset_dialog.EndModal(reset_dialog.GetAffirmativeId())
        return True

    @phase_timeout(5.0)
    def _phase_18(self, w: ControlWindow):
        """
        Wait reset done.
        """
        reset_dialog = self._get_modal_dialog(w, ResetProgressDialog)
        done_dialog = self._get_modal_dialog(reset_dialog, wx.GenericMessageDialog)
        assert done_dialog.GetMessage() == gettext("Nano instance has been successfully reset!")
        assert w._inhibits_sleep is False
        done_dialog.EndModal(reset_dialog.GetAffirmativeId())
        return True

    def _phase_19(self, w: ControlWindow):
        """
        Do new login.
        """
        login_widget = self._check_waiting_login_get_central_widget(w)
        login_widget._email_field.SetValue(self.email)
        login_widget._password_field.SetValue(self.password)
        login_widget.ProcessEvent(wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, login_widget._login_btn.GetId()))
        return True

    @phase_timeout(5.0)
    def _phase_20(self, w: ControlWindow):
        """
        Check successful login effects and setup wizard.
        """
        config = w.core_interface.get_config()
        assert (v := config["state"].get("gateway_status")) == "connected", str(v)
        assert isinstance(w.central_widget(), ControlStatus)
        d = self._get_modal_dialog(w, SimpleConfigurationWizard)
        assert d.GetCurrentPage() is d.intro_page
        assert w._inhibits_sleep is True
        self._find_send_command_event(d, wx.ID_CANCEL, wx.wxEVT_COMMAND_BUTTON_CLICKED)
        return True

    @phase_timeout(5.0)
    def _phase_21(self, w: ControlWindow):
        """
        Open next config window.
        """
        d = self._get_modal_dialog(w, SimpleConfigurationWizard, require=False)
        assert not d
        w.QueueEvent(wx.CommandEvent(wx.wxEVT_MENU, w._configure_drive_menu_id))
        return True

    @phase_timeout(5.0)
    def _phase_22(self, w: ControlWindow):
        """
        Wait for drive manager to open and create a drive.
        """
        d = self._get_modal_dialog(w, DriveConfigManager)
        # NOTE: we don't simulate directory listing & selection
        # d.ProcessEvent(wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, d.create_drive.GetId()))
        assert w.core_interface.get_config()["remote"]["drives"] == []
        d._do_create_drive(self.tmp_dir)
        return True

    @phase_timeout(5.0)
    def _phase_23(self, w: ControlWindow):
        """
        Wait for drive to be created. Then start attaching.
        """
        d = self._get_modal_dialog(w, DriveConfigManager)
        drive_item = d.tree.GetFirstChild(d.tree.GetRootItem())[0]
        assert d.tree.GetItemText(drive_item) == self.tmp_dir
        assert w.core_interface.get_config()["remote"]["drives"] == [{"path": self.tmp_dir}]
        d.tree.SelectItem(drive_item)
        d.QueueEvent(wx.CommandEvent(wx.wxEVT_MENU, d._attach_id))
        return True

    @phase_timeout(5.0)
    def _phase_24(self, w: ControlWindow):
        """
        Wait for drive to be created. Then start attaching.
        """
        d = self._get_modal_dialog(w, DriveAttachDialog)
        assert not d._accept_button.IsEnabled()
        d._data.SetValue(base64.urlsafe_b64encode(msgpack_dumps(self.test_block_data)).decode("ascii"))
        assert d._accept_button.IsEnabled()
        d.ProcessEvent(wx.CommandEvent(wx.wxEVT_COMMAND_BUTTON_CLICKED, d._accept_button.GetId()))
        return True

    @phase_timeout(5.0)
    def _phase_25(self, w: ControlWindow):
        """
        Wait for room to be attached, then detach it.
        """
        d = self._get_modal_dialog(w, DriveAttachDialog, require=False)
        assert not d
        d = self._get_modal_dialog(w, DriveConfigManager)
        assert w.core_interface.get_config()["remote"]["drives"] == [
            {"path": self.tmp_dir, "rooms": [self.test_block_data["id"]]}
        ]
        drive_item = d.tree.GetFirstChild(d.tree.GetRootItem())[0]
        room_item = d.tree.GetFirstChild(drive_item)[0]
        assert d.tree.GetItemText(room_item) == self.test_block_data["id"]
        d.tree.SelectItem(room_item)
        d.ProcessEvent(wx.CommandEvent(wx.wxEVT_MENU, d._detach_room_id))
        return True

    @phase_timeout(5.0)
    def _phase_26(self, w: ControlWindow):
        """
        Wait for room to be detached, then delete the drive.
        """
        d = self._get_modal_dialog(w, DriveConfigManager)
        assert w.core_interface.get_config()["remote"]["drives"] == [{"path": self.tmp_dir, "rooms": []}]
        drive_item = d.tree.GetFirstChild(d.tree.GetRootItem())[0]
        assert d.tree.GetChildrenCount(drive_item, False) == 0
        assert d.tree.GetItemText(drive_item) == self.tmp_dir
        d.tree.SelectItem(drive_item)
        d.ProcessEvent(wx.CommandEvent(wx.wxEVT_MENU, d._delete_drive_id))
        return True

    @phase_timeout(5.0)
    def _phase_27(self, w: ControlWindow):
        """
        Wait for drive deletion.
        """
        d = self._get_modal_dialog(w, DriveConfigManager)
        assert w.core_interface.get_config()["remote"]["drives"] == []
        assert d.tree.GetChildrenCount(d.tree.GetRootItem(), False) == 0
        d.EndModal(wx.ID_OK)
        return True

    @phase_timeout(5.0)
    def _phase_28(self, w: ControlWindow):
        """
        Wait close, then open about dialog.
        """
        d = self._get_modal_dialog(w, DriveConfigManager, require=False)
        assert not d
        w.QueueEvent(wx.CommandEvent(wx.wxEVT_MENU, wx.ID_ABOUT))
        return True

    @phase_timeout(5.0)
    def _phase_29(self, w: ControlWindow):
        """
        Wait for about dialog to open and switch to CC tab.
        """
        d = self._get_modal_dialog(w, AboutDialog)
        assert d._title_label.GetLabelText().find(__version__) >= 0
        assert d._tabs.GetCurrentPage() is d._license_text
        d._tabs.SetSelection(2)
        return True

    @phase_timeout(5.0)
    def _phase_30(self, w: ControlWindow):
        """
        Check default CC state and request change.
        """
        d = self._get_modal_dialog(w, AboutDialog)
        assert d._tabs.GetCurrentPage() is d._cc_text
        cc_text = d._cc_text.GetValue()
        assert cc_text.find(__version__) >= 0
        assert cc_text.find(d.cc_config_appropriate) == -1
        assert cc_text.find(d.cc_config_not_correct) >= 0
        w.core_interface.core_request(
            [
                MSG_TYPE_REQUEST_CONFIG_UPDATE,
                msgpack_dumps({"_handler_key": "require_explicit_peer_trust", "require_explicit_peer_trust": True}),
            ]
        )
        return True

    @phase_timeout(5.0)
    def _phase_31(self, w: ControlWindow):
        """
        Check changed CC state, then revert.
        """
        d = self._get_modal_dialog(w, AboutDialog)
        assert d._tabs.GetCurrentPage() is d._cc_text
        cc_text = d._cc_text.GetValue()
        assert cc_text.find(__version__) >= 0
        assert cc_text.find(d.cc_config_appropriate) >= 0
        assert cc_text.find(d.cc_config_not_correct) == -1
        w.core_interface.core_request(
            [
                MSG_TYPE_REQUEST_CONFIG_UPDATE,
                msgpack_dumps({"_handler_key": "require_explicit_peer_trust", "require_explicit_peer_trust": False}),
            ]
        )
        return True

    @phase_timeout(5.0)
    def _phase_32(self, w: ControlWindow):
        """
        Check reverted CC state then close.
        """
        d = self._get_modal_dialog(w, AboutDialog)
        assert d._tabs.GetCurrentPage() is d._cc_text
        cc_text = d._cc_text.GetValue()
        assert cc_text.find(__version__) >= 0
        assert cc_text.find(d.cc_config_appropriate) == -1
        assert cc_text.find(d.cc_config_not_correct) >= 0
        d.EndModal(wx.ID_OK)
        return True

    @phase_timeout(5.0)
    def _phase_33(self, w: ControlWindow):
        """
        Wait close.
        """
        d = self._get_modal_dialog(w, AboutDialog, require=False)
        assert not d
        return True
