import wx
import zxcvbn
from clarabot_crypto.auth.common import generate_client_salt_input

from clarabot_nano.common.core.message import MSG_TYPE_GATEWAY_CHANGE_PASSWORD
from clarabot_nano.control.client import WxCoreInterface
from clarabot_nano.control.common import password_change_command_result
from clarabot_nano.control.icon import get_wx_nano_icon
from clarabot_nano.control.two_factor import TwoFactorGatewayRequestMixin
from clarabot_nano.control.utility import Slots


class ChangeAccountPasswordDialog(wx.Dialog, Slots, TwoFactorGatewayRequestMixin):
    def __init__(self, core_interface: WxCoreInterface, parent):
        wx.Dialog.__init__(self, parent, wx.ID_ANY, gettext("Change account password"))
        Slots.__init__(self)
        TwoFactorGatewayRequestMixin.__init__(self, core_interface)
        self.SetIcon(get_wx_nano_icon())
        self._old_bs = wx.Size(0, 0)

        layout = wx.BoxSizer(wx.VERTICAL)
        label = wx.StaticText(
            self, wx.ID_ANY, gettext("Changing the password will log-out all user-sessions of the account.")
        )
        layout.Add(label, 0, wx.ALL | wx.EXPAND, 8)

        self._password_field = wx.TextCtrl(self, style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER)
        self._password_field.SetHint(gettext("New password"))
        self.Bind(wx.EVT_TEXT, self._on_password_changed, self._password_field)
        self.Bind(wx.EVT_TEXT_ENTER, self._on_password_return, self._password_field)
        layout.Add(self._password_field, 0, wx.ALL | wx.EXPAND, 10)
        self._password_field_2 = wx.TextCtrl(self, style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER)
        self._password_field_2.SetHint(gettext("Verify new password"))
        self.Bind(wx.EVT_TEXT, self._on_password_2_changed, self._password_field_2)
        self.Bind(wx.EVT_TEXT_ENTER, self._on_password_2_return, self._password_field_2)
        layout.Add(self._password_field_2, 0, wx.ALL | wx.EXPAND, 12)

        self._feedback_label = wx.StaticText(self, wx.ID_ANY, "")
        layout.Add(self._feedback_label, 1, wx.LEFT | wx.RIGHT | wx.EXPAND, 16)

        buttons = self.CreateButtonSizer(wx.OK | wx.CANCEL)
        if buttons:
            layout.Add(buttons, 0, wx.ALL | wx.EXPAND, 4)
        self._accept_button = self.FindWindow(self.GetAffirmativeId())
        self.Bind(wx.EVT_BUTTON, self._on_accept, self._accept_button)

        self.SetSizer(layout)
        self._grow_fit()

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

        self._password_field.SetFocus()
        self._on_password_changed()  # init feedback

    def _on_password_changed(self, *args):
        self._do_feedback()

    def _do_feedback(self):
        pw = self._password_field.GetValue()
        feedback_lines = []
        acceptable = True
        if len(pw) < 10:
            acceptable = False
            feedback_lines.append(gettext("Length of at least 10 characters are required."))
        if pw:
            pwa = zxcvbn.zxcvbn(pw)
            if pwa["score"] < 1:
                acceptable = False
                feedback_lines.append(gettext("This password is too weak."))
            fb = pwa["feedback"]
            if fb["warning"]:
                acceptable = False
                feedback_lines.append(fb["warning"])
            feedback_lines.extend(fb["suggestions"])
            feedback_lines.append(
                "Estimated time to be cracked: " + pwa["crack_times_display"]["offline_fast_hashing_1e10_per_second"]
            )
        self._password_field_2.Enable(acceptable)
        if acceptable:
            if pw != self._password_field_2.GetValue():
                acceptable = False
                feedback_lines.insert(0, gettext("The passwords do not match."))
        else:
            self._password_field_2.Clear()
        self._accept_button.Enable(acceptable)
        self._feedback_label.SetLabelText("\n".join(feedback_lines + [""] * (5 - len(feedback_lines))))
        self._grow_fit()

    def _on_password_return(self, event):
        if self._password_field_2.IsEnabled():
            self._password_field_2.SetFocus()

    def _on_password_2_changed(self, event):
        if self._password_field_2.IsEnabled():
            self._do_feedback()

    def _on_password_2_return(self, event):
        if self.FindWindow(self.GetAffirmativeId()).IsEnabled():
            self._on_accept()

    def _grow_fit(self):
        self._old_bs.IncTo(self.GetBestSize())
        self.SetSize(self._old_bs)

    def _on_accept(self, *args):
        self._start_gateway_request(
            MSG_TYPE_GATEWAY_CHANGE_PASSWORD,
            {"password": self._password_field.GetValue(), "client_salt_input": generate_client_salt_input()},
            gettext("Requesting password change..."),
        )

    def _set_feedback_text(self, text: str):
        self._feedback_label.SetLabelText(password_change_command_result(text))

    def _enable_ui(self, b: bool):
        self._password_field.Enable(b)
        self._password_field_2.Enable(b)
        self._accept_button.Enable(b)

    def _gateway_request_ended(self, response_received, result):
        if result == "password change successful":
            with wx.GenericMessageDialog(
                self, self._feedback_label.GetLabelText(), gettext("Done"), wx.ICON_INFORMATION | wx.OK
            ) as d:
                d.ShowModal()
            self.EndModal(wx.ID_OK)
