class PinConfirmationLayout(QVBoxLayout, ErrorMessageMixin, InputFieldMixin):

    def __init__(self, parent, email, error_msg=''):
        super(). __init__()
        self.parent = parent
        self.resend_strategy = parent.resend_strategy
        self.cool_down_thread = parent.cool_down_thread

        label = QLabel(_('To confirm the request, please enter the code we sent to') + f"<br><b>{email}</b>")
        label.setWordWrap(True)
        hbox = QHBoxLayout()
        code_label = QLabel(_('Code:'))
        pin_edit = PinInputFiled()
        hbox.addWidget(code_label)
        hbox.addWidget(pin_edit)
        pin_edit.textChanged.connect(self.input_edited)
        self.input_edit = pin_edit

        self.addWidget(label)
        self.addSpacing(10)
        self.addLayout(hbox)
        self.error_label = QLabel()
        self.error_label.setWordWrap(True)
        self.error_label.setVisible(False)
        if error_msg:
            self.set_error(error_msg)

        self.send_request_thread = TaskThread(None)
        if self.cool_down_thread.is_running():
            self.resend_button = QPushButton(self.cool_down_thread.get_formatted_left_time())
            self.resend_button.setEnabled(False)
        else:
            self.resend_button = QPushButton(_('Resend'))
        hbox.addStretch(1)
        hbox.addWidget(self.resend_button)
        self.resend_button.clicked.connect(self.resend_request)
        self.addSpacing(5)
        self.addLayout(hbox)
        self.addSpacing(5)
        self.addWidget(self.error_label)

    def on_error(self, errors):
        self.resend_strategy.on_error(errors[1])
        self.set_error(str(errors[1]))

    def resend_request(self):
        self.clear_error()
        self.cool_down_thread.run()
        self.send_request_thread.add(
            task=lambda: self.resend_strategy.resend(),
            on_success=lambda *args, **kwargs: None,
            on_error=self.on_error
        )

    def pin(self):
        return self.input_edit.text()

    def terminate_thread(self):
        self.send_request_thread.terminate()
class CoolDownThread:
    RESEND_COOL_DOWN_TIME = 30

    def __init__(self):
        self.elapsed_time = 0
        self.pin_layout = None
        self.thread = None

    def register_layout(self, layout: PinConfirmationLayout):
        self.pin_layout = layout

    def unregister_layout(self):
        self.pin_layout = None

    def _set_resend_button_status(self, text: str, enabled: bool):
        if self.pin_layout:
            self.pin_layout.resend_button.setText(text)
            self.pin_layout.resend_button.setEnabled(enabled)

    def get_left_time(self) -> int:
        return int(self.RESEND_COOL_DOWN_TIME - self.elapsed_time)

    def get_formatted_left_time(self) -> str:
        return f'{self.get_left_time():>2}s'

    def cool_down_task(self):
        t0 = datetime.datetime.now()
        while True:
            self.elapsed_time = (datetime.datetime.now() - t0).total_seconds()
            if self.get_left_time() < 1:
                break
            self._set_resend_button_status(self.get_formatted_left_time(), False)
            time.sleep(1)

    def on_success(self, *args, **kwargs):
        self._set_resend_button_status(_('Resend'), True)

    def run(self):
        self.thread = TaskThread(None)
        self.thread.add(
            task=self.cool_down_task,
            on_success=self.on_success,
            on_error=lambda *args, **kwargs: None,
        )

    def is_running(self) -> bool:
        return bool(self.thread and self.get_left_time() > 0)

    def terminate(self):
        self.elapsed_time = 0
        if self.thread:
            self.thread.terminate()