#
# Bitcoin Safe
# Copyright (C) 2024 Andreas Griffin
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of version 3 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see https://www.gnu.org/licenses/gpl-3.0.html
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.


import logging
from abc import abstractmethod

from bitcoin_safe.html import html_f
from bitcoin_safe.i18n import translate
from bitcoin_safe.signals import Signals

logger = logging.getLogger(__name__)

import enum
from math import ceil
from typing import Callable, Dict, List, Optional

import bdkpython as bdk
import numpy as np
from PyQt6.QtCore import QObject, QSize, Qt, pyqtSignal
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import (
    QDialogButtonBox,
    QHBoxLayout,
    QLabel,
    QMessageBox,
    QPushButton,
    QSpacerItem,
    QTabWidget,
    QVBoxLayout,
    QWidget,
)

from bitcoin_safe.gui.qt.descriptor_ui import KeyStoreUIs
from bitcoin_safe.gui.qt.dialogs import question_dialog
from bitcoin_safe.gui.qt.qt_wallet import QTWallet, QtWalletBase
from bitcoin_safe.gui.qt.tutorial_screenshots import (
    ScreenshotsExportXpub,
    ScreenshotsGenerateSeed,
    ScreenshotsRegisterMultisig,
    ScreenshotsTutorial,
    ScreenshotsViewSeed,
)

from ...pdfrecovery import make_and_open_pdf
from ...pythonbdk_types import Recipient
from ...tx import TxUiInfos
from ...util import Satoshis
from .qr_components.quick_receive import ReceiveGroup
from .spinning_button import SpinningButton
from .step_progress_bar import StepProgressContainer, TutorialWidget
from .taglist.main import hash_color
from .util import (
    Message,
    MessageType,
    add_centered,
    add_centered_icons,
    caught_exception_message,
    create_button_box,
    icon_path,
    one_time_signal_connection,
    open_website,
)


class TutorialStep(enum.Enum):
    buy = enum.auto()
    generate = enum.auto()
    import_xpub = enum.auto()
    backup_seed = enum.auto()
    validate_backup = enum.auto()
    receive = enum.auto()
    register = enum.auto()
    reset = enum.auto()
    send = enum.auto()
    send2 = enum.auto()
    send3 = enum.auto()
    send4 = enum.auto()
    send5 = enum.auto()
    send6 = enum.auto()
    send7 = enum.auto()
    send8 = enum.auto()
    send9 = enum.auto()
    send10 = enum.auto()
    distribute = enum.auto()


class FloatingButtonBar(QDialogButtonBox):
    class TxSendStatus(enum.Enum):
        not_filled = enum.auto()
        filled = enum.auto()
        finalized = enum.auto()
        sent = enum.auto()

    def __init__(
        self,
        fill_tx: Callable,
        create_tx: Callable,
        go_to_next_index: Callable,
        go_to_previous_index: Callable,
        signals: Signals,
    ) -> None:
        super().__init__()
        self.status = None
        self._fill_tx = fill_tx
        self._create_tx = create_tx
        self._go_to_next_index = go_to_next_index
        self._go_to_previous_index = go_to_previous_index
        self.signals = signals

        self.signals.language_switch.connect(self.updateUi)

    def set_visibilities(self):
        self.tutorial_button_prefill.setVisible(self.status in [self.TxSendStatus.not_filled])
        self.button_create_tx.setVisible(self.status in [self.TxSendStatus.filled])
        self.tutorial_button_prev_step.setVisible(True)
        self.button_yes_it_is_in_hist.setVisible(
            self.status in [self.TxSendStatus.finalized, self.TxSendStatus.sent]
        )

    def set_status(self, status=TxSendStatus):
        if self.status == status:
            return
        self.status = status
        self.set_visibilities()

    def fill_tx(self):
        self._fill_tx()
        self.set_status(self.TxSendStatus.filled)

    def create_tx(self):
        # before do _create_tx, setup a 1 time connection
        # so I can catch the tx and ensure that TxSendStatus == finalized
        # just in case the suer clicked "go back"
        def catch_txid(tx: bdk.Transaction):
            self.set_status(self.TxSendStatus.finalized)

        one_time_signal_connection(self.signals.signal_broadcast_tx, catch_txid)

        self._create_tx()
        self.set_status(self.TxSendStatus.finalized)

    def go_to_next_index(self):
        self._go_to_next_index()
        self.set_status(self.TxSendStatus.not_filled)

    def go_to_previous_index(self):
        self._go_to_previous_index()
        self.set_status(self.TxSendStatus.not_filled)

    def fill(self) -> QDialogButtonBox:
        self.setVisible(False)

        self.tutorial_button_prefill = QPushButton()
        self.tutorial_button_prefill.clicked.connect(self.fill_tx)
        self.addButton(self.tutorial_button_prefill, QDialogButtonBox.ButtonRole.AcceptRole)

        self.button_create_tx = QPushButton()
        self.button_create_tx.clicked.connect(self.create_tx)
        self.addButton(self.button_create_tx, QDialogButtonBox.ButtonRole.AcceptRole)

        self.button_yes_it_is_in_hist = QPushButton()
        self.button_yes_it_is_in_hist.setVisible(False)
        self.button_yes_it_is_in_hist.clicked.connect(self.go_to_next_index)
        self.addButton(self.button_yes_it_is_in_hist, QDialogButtonBox.ButtonRole.AcceptRole)

        self.tutorial_button_prev_step = QPushButton()
        self.tutorial_button_prev_step.clicked.connect(self.go_to_previous_index)
        self.addButton(self.tutorial_button_prev_step, QDialogButtonBox.ButtonRole.RejectRole)

        self.set_status(self.TxSendStatus.not_filled)

    def updateUi(self):

        self.tutorial_button_prefill.setText(self.tr("Fill the transaction fields"))
        self.button_create_tx.setText(self.tr("Create Transaction"))
        self.button_yes_it_is_in_hist.setText(self.tr("Yes, I see the transaction in the history"))
        self.tutorial_button_prev_step.setText(self.tr("Previous Step"))


class TabInfo:
    def __init__(
        self,
        container: StepProgressContainer,
        wallet_tabs: QTabWidget,
        qtwalletbase: QtWalletBase,
        go_to_next_index: Callable,
        go_to_previous_index: Callable,
        floating_button_box: FloatingButtonBar,
        signal_create_wallet,
        max_test_fund: int,
        qt_wallet: QTWallet = None,
    ) -> None:
        self.container = container
        self.wallet_tabs = wallet_tabs
        self.qtwalletbase = qtwalletbase
        self.go_to_next_index = go_to_next_index
        self.go_to_previous_index = go_to_previous_index
        self.floating_button_box = floating_button_box
        self.signal_create_wallet = signal_create_wallet
        self.qt_wallet = qt_wallet
        self.max_test_fund = max_test_fund


class BaseTab(QObject):
    def __init__(self, refs: TabInfo) -> None:
        super().__init__(parent=refs.container)
        self.refs = refs

        self.buttonbox, self.buttonbox_buttons = create_button_box(
            self.refs.go_to_next_index,
            self.refs.go_to_previous_index,
            ok_text="",
            cancel_text="",
        )
        self.refs.qtwalletbase.signals.language_switch.connect(self.updateUi)

    @abstractmethod
    def create(self) -> TutorialWidget:
        pass

    def updateUi(self):
        self.buttonbox_buttons[0].setText(translate("basetab", "Next step"))
        self.buttonbox_buttons[1].setText(translate("basetab", "Previous Step"))
        self.refs.floating_button_box.updateUi()

    def num_keystores(self) -> int:
        return self.refs.qtwalletbase.get_mn_tuple()[1]

    def get_never_label_text(self) -> str:
        return html_f(
            html_f(
                translate("tutorial", "Never share the 24 secret words with anyone!"),
                p=True,
                size=12,
                color="red",
            )
            + html_f(
                translate("tutorial", "Never type them into any computer or cellphone!"),
                p=True,
                size=12,
                color="red",
            )
            + html_f(translate("tutorial", "Never make a picture of them!"), p=True, size=12, color="red"),
            add_html_and_body=True,
        )


class BuyHardware(BaseTab):
    def create(self) -> TutorialWidget:
        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        add_centered_icons(
            ["coldcard-only.svg"] * int(np.ceil(self.num_keystores() / 2))
            + ["usb-stick.svg"] * int(np.floor(self.num_keystores() / 2)),
            widget,
            max_sizes=[(100, 80)] * self.num_keystores(),
        )

        widget.layout().itemAt(0).widget().setMaximumWidth(150)

        right_widget = QWidget()
        right_widget.setLayout(QVBoxLayout())
        right_widget.layout().setAlignment(Qt.AlignmentFlag.AlignVCenter)
        widget.layout().addWidget(right_widget)

        self.label_buy = QLabel(widget)
        self.label_buy.setWordWrap(True)
        right_widget.layout().addWidget(self.label_buy)

        self.button_buycoldcard = QPushButton()
        self.button_buycoldcard.setIcon(QIcon(icon_path("coldcard-only.svg")))
        self.button_buycoldcard.clicked.connect(
            lambda: open_website("https://store.coinkite.com/promo/8BFF877000C34A86F410")
        )
        right_widget.layout().addWidget(self.button_buycoldcard)
        self.button_buycoldcard.setIconSize(QSize(32, 32))  # Set the icon size to 64x64 pixels

        self.button_buybitbox = QPushButton()
        self.button_buybitbox.setIcon(QIcon(icon_path("usb-stick.svg")))
        self.button_buybitbox.clicked.connect(
            lambda: open_website("https://shiftcrypto.ch/bitbox02/?ref=MOB4dk7gpm")
        )
        self.button_buybitbox.setIconSize(QSize(32, 32))  # Set the icon size to 64x64 pixels
        right_widget.layout().addWidget(self.button_buybitbox)

        right_widget.layout().addItem(QSpacerItem(1, 40))

        self.label_turn_on = QLabel(widget)
        self.label_turn_on.setWordWrap(True)

        right_widget.layout().addWidget(self.label_turn_on)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, self.buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()
        self.label_buy.setText(
            html_f(self.tr("Do you need to buy a hardware signer?"), add_html_and_body=True, p=True, size=12)
        )

        self.button_buybitbox.setText(self.tr("Buy a {name}").format(name="Bitbox02\nBitcoin Only Edition"))
        self.button_buycoldcard.setText(self.tr("Buy a Coldcard\n5% off"))
        self.label_turn_on.setText(
            html_f(
                self.tr("Turn on your {n} hardware signers").format(n=self.num_keystores())
                if self.num_keystores() > 1
                else self.tr("Turn on your hardware signer"),
                add_html_and_body=True,
                p=True,
                size=12,
            ),
        )


class GenerateSeed(BaseTab):
    def create(self) -> TutorialWidget:

        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        self.screenshot = ScreenshotsGenerateSeed()
        widget.layout().addWidget(self.screenshot)

        self.never_label = QLabel()
        self.never_label.setWordWrap(True)
        self.never_label.setMinimumWidth(300)
        widget.layout().addWidget(self.never_label)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, self.buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()
        self.screenshot.updateUi()
        self.never_label.setText(self.get_never_label_text())


class ValidateBackup(BaseTab):
    def create(self) -> TutorialWidget:

        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        self.screenshot = ScreenshotsViewSeed()
        widget.layout().addWidget(self.screenshot)
        self.never_label = QLabel()
        self.never_label.setWordWrap(True)
        self.never_label.setMinimumWidth(300)
        widget.layout().addWidget(self.never_label)

        buttonbox = QDialogButtonBox()
        self.custom_yes_button = QPushButton()
        self.custom_yes_button.clicked.connect(self.refs.go_to_next_index)
        buttonbox.addButton(self.custom_yes_button, QDialogButtonBox.ButtonRole.AcceptRole)
        self.custom_cancel_button = QPushButton()
        self.custom_cancel_button.clicked.connect(self.refs.go_to_previous_index)
        buttonbox.addButton(self.custom_cancel_button, QDialogButtonBox.ButtonRole.RejectRole)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()
        self.custom_yes_button.setText(self.tr("Yes, I am sure all 24 words are correct"))
        self.custom_cancel_button.setText(self.tr("Previous Step"))
        self.screenshot.updateUi()
        self.never_label.setText(self.get_never_label_text())


class ImportXpubs(BaseTab):
    def create(self) -> TutorialWidget:

        widget = QWidget()
        widget.setLayout(QVBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        self.screenshot = ScreenshotsExportXpub()
        widget.layout().addWidget(self.screenshot)

        self.label_import = QLabel()
        # handle protowallet and qt_wallet differently:
        self.button_create_wallet = QPushButton("")
        if self.refs.qt_wallet:
            # show the full walet descriptor tab below
            pass

        else:
            # integrater the KeyStoreUIs into the tutorials, hide wallet_tabs

            self.label_import.setFont(self.screenshot.title.font())
            widget.layout().addWidget(self.label_import)

            # this is used in TutorialStep.import_xpub
            self.keystore_uis = KeyStoreUIs(
                get_editable_protowallet=self.refs.qtwalletbase.get_editable_protowallet,
                get_address_type=lambda: self.refs.qtwalletbase.get_editable_protowallet().address_type,
                signals_min=self.refs.qtwalletbase.signals,
            )
            self.keystore_uis.setCurrentIndex(0)
            widget.layout().addWidget(self.keystore_uis)

            def create_wallet():
                try:
                    self.keystore_uis.set_protowallet_from_keystore_ui()
                    self.refs.qtwalletbase.get_editable_protowallet().tutorial_index = (
                        self.refs.container.current_index() + 1
                    )
                    self.refs.signal_create_wallet.emit()
                except Exception as e:
                    caught_exception_message(e)

            # hide the next button
            self.buttonbox_buttons[0].setHidden(True)
            # and add the create wallet button
            self.buttonbox_buttons.append(self.button_create_wallet)
            self.buttonbox.addButton(self.button_create_wallet, QDialogButtonBox.ButtonRole.AcceptRole)
            self.button_create_wallet.clicked.connect(create_wallet)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, self.buttonbox, buttonbox_always_visible=False
        )

        def callback():
            self.refs.wallet_tabs.setCurrentWidget(self.refs.qtwalletbase.wallet_descriptor_tab)
            tutorial_widget.synchronize_visiblity(
                self.refs.wallet_tabs, set_also_visible=bool(self.refs.qt_wallet)
            )

        tutorial_widget.set_callback(callback)
        tutorial_widget.synchronize_visiblity(
            self.refs.wallet_tabs, set_also_visible=bool(self.refs.qt_wallet)
        )
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()
        self.label_import.setText(self.tr("2. Import wallet information into Bitcoin Safe"))
        if self.refs.qt_wallet:
            self.button_create_wallet.setText(self.tr("Skip step"))
        else:
            self.button_create_wallet.setText(self.tr("Next step"))
        self.buttonbox_buttons[1].setText(self.tr("Previous Step"))
        self.screenshot.updateUi()


class BackupSeed(BaseTab):
    def create(self) -> TutorialWidget:

        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        add_centered_icons(
            ["descriptor-backup.svg"],
            widget,
            max_sizes=[(100 * self.num_keystores(), 120)],
        )

        self.label_print_instructions = QLabel(widget)
        self.label_print_instructions.setWordWrap(True)

        widget.layout().addWidget(self.label_print_instructions)

        def do_pdf():
            if not self.refs.qt_wallet:
                Message(self.tr("Please complete the previous steps."))
                return
            make_and_open_pdf(self.refs.qt_wallet.wallet)

        # button = create_button(
        #     "Print the descriptor", icon_path("pdf-file.svg"), widget, layout
        # )
        # button.setMaximumWidth(150)
        # button.clicked.connect(do_pdf)

        buttonbox = QDialogButtonBox()
        self.custom_yes_button = QPushButton()
        self.custom_yes_button.setIcon(QIcon(icon_path("print.svg")))
        self.custom_yes_button.clicked.connect(do_pdf)
        self.custom_yes_button.clicked.connect(self.refs.go_to_next_index)
        buttonbox.addButton(self.custom_yes_button, QDialogButtonBox.ButtonRole.AcceptRole)
        self.custom_cancel_button = QPushButton()
        self.custom_cancel_button.clicked.connect(self.refs.go_to_previous_index)
        buttonbox.addButton(self.custom_cancel_button, QDialogButtonBox.ButtonRole.RejectRole)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()

        self.custom_yes_button.setText(self.tr("Print recovery sheet"))
        self.custom_cancel_button.setText(self.tr("Previous Step"))

        self.label_print_instructions.setText(
            html_f(
                f"""<ol>
        <li>{self.tr('Print the pdf (it also contains the wallet descriptor)')}</li>
        <li>{self.tr('Write each 24-word seed onto the printed pdf.') if self.num_keystores()>1 else self.tr('Write the 24-word seed onto the printed pdf.') }</li>
        </ol>""",
                add_html_and_body=True,
                p=True,
                size=12,
            )
        )


class ReceiveTest(BaseTab):
    def create(self) -> TutorialWidget:

        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(10, 0, 0, 0)  # Left, Top, Right, Bottom margins
        widget.layout().setSpacing(20)
        self.quick_receive: Optional[ReceiveGroup] = None

        if self.refs.qt_wallet:
            category = self.refs.qt_wallet.wallet.labels.get_default_category()
            address_info = self.refs.qt_wallet.wallet.get_unused_category_address(category)
            self.quick_receive = ReceiveGroup(
                category,
                hash_color(category).name(),
                address_info.address.as_string(),
                address_info.address.to_qr_uri(),
            )
            self.quick_receive.setMaximumHeight(300)
            widget.layout().addWidget(self.quick_receive)
        else:
            add_centered_icons(["receive.svg"], widget, max_sizes=[(50, 80)])
            widget.layout().itemAt(0).widget().setMaximumWidth(150)

        self.label_receive_description = QLabel(widget)
        self.label_receive_description.setWordWrap(True)

        widget.layout().addWidget(self.label_receive_description)

        buttonbox = QDialogButtonBox()
        self.next_button = QPushButton()
        self.next_button.clicked.connect(self.refs.go_to_next_index)
        buttonbox.addButton(self.next_button, QDialogButtonBox.ButtonRole.AcceptRole)
        self.check_button = SpinningButton("")
        buttonbox.addButton(self.check_button, QDialogButtonBox.ButtonRole.AcceptRole)
        self.cancel_button = QPushButton()
        self.cancel_button.clicked.connect(self.refs.go_to_previous_index)
        buttonbox.addButton(self.cancel_button, QDialogButtonBox.ButtonRole.RejectRole)

        self.next_button.setHidden(True)

        def on_utxo_update(sync_status):
            if not self.refs.qt_wallet:
                return
            txos = self.refs.qt_wallet.wallet.get_all_txos(include_not_mine=False)
            self.check_button.setHidden(bool(txos))
            self.next_button.setHidden(not bool(txos))
            if txos:
                Message(
                    self.tr("Received {amount}").format(
                        amount=Satoshis(
                            txos[0].txout.value, self.refs.qt_wallet.wallet.network
                        ).str_with_unit()
                    )
                )

        def start_sync():
            if not self.refs.qt_wallet:
                Message(self.tr("No wallet setup yet"), type=MessageType.Error)
                return

            self.refs.qt_wallet.sync()
            self.check_button.set_enable_signal(self.refs.qtwalletbase.signals.utxos_updated)
            one_time_signal_connection(self.refs.qtwalletbase.signals.utxos_updated, on_utxo_update)

        self.check_button.clicked.connect(start_sync)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()
        test_amount = (
            f"(less than {Satoshis( self.refs.max_test_fund, self.refs.qt_wallet.wallet.network).str_with_unit()}) "
            if self.refs.qt_wallet
            else ""
        )
        self.label_receive_description.setText(
            html_f(
                self.tr("Receive a small amount {test_amount} to an address of this wallet").format(
                    test_amount=test_amount
                ),
                add_html_and_body=True,
                p=True,
                size=12,
            )
        )
        self.next_button.setText(self.tr("Next step"))
        self.check_button.setText(self.tr("Check if received"))
        self.cancel_button.setText(self.tr("Previous Step"))


class RegisterMultisig(BaseTab):
    def create(self) -> TutorialWidget:
        def create_register_coldcard_button() -> QPushButton:
            button_register_coldcard_quorum = QPushButton(
                self.tr("""Export file to register the multisig on Coldcard""")
            )
            # button_style = """
            # QPushButton {
            #     padding: 20px 10px; /* Adjust the padding values as needed */
            # }
            # """
            # button_register_quorum.setStyleSheet(button_style)

            def export_wallet_for_coldcard():
                if self.refs.qt_wallet:
                    self.refs.qt_wallet.export_wallet_for_coldcard()

            button_register_coldcard_quorum.clicked.connect(export_wallet_for_coldcard)
            return button_register_coldcard_quorum

        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        # width = 300
        # svg_widgets = add_centered_icons(["reset-signer.svg"], widget, max_sizes=[(width, 120)])
        # widget.layout().itemAt(0).widget().setMaximumWidth(width)

        self.groupbox1 = ScreenshotsTutorial()
        tab = QWidget()
        tab.setLayout(QVBoxLayout())
        add_centered([create_register_coldcard_button()], tab, direction="v")
        self.groupbox1.sync_tab.addTab(tab, "Coldcard")
        widget.layout().addWidget(self.groupbox1)

        self.screenshot = ScreenshotsRegisterMultisig()
        widget.layout().addWidget(self.screenshot)

        buttonbox = QDialogButtonBox()
        self.custom_yes_button = QPushButton()
        self.custom_yes_button.clicked.connect(self.refs.go_to_next_index)
        buttonbox.addButton(self.custom_yes_button, QDialogButtonBox.ButtonRole.AcceptRole)
        self.custom_cancel_button = QPushButton()
        self.custom_cancel_button.clicked.connect(self.refs.go_to_previous_index)
        buttonbox.addButton(self.custom_cancel_button, QDialogButtonBox.ButtonRole.RejectRole)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)

        def callback():
            if not self.refs.qt_wallet:
                return
            balance = self.refs.qt_wallet.wallet.bdkwallet.get_balance().total
            if balance > self.refs.max_test_fund:
                Message(
                    self.tr(
                        "Your balance {balance} is greater than a maximally allowed test amount of {amount}!\nPlease do the hardware signer reset only  with a lower balance!  (Send some funds out before)"
                    ).format(
                        balance=Satoshis(balance, self.refs.qt_wallet.wallet.network).str_with_unit(),
                        amount=Satoshis(
                            self.refs.max_test_fund, self.refs.qt_wallet.wallet.network
                        ).str_with_unit(),
                    ),
                    type=MessageType.Warning,
                )

        tutorial_widget.set_callback(callback)
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()
        self.groupbox1.title.setText(self.tr("1. Export wallet descriptor"))
        self.custom_yes_button.setText(
            self.tr("Yes, I registered the multisig on the {n} hardware signer").format(
                n=self.num_keystores()
            )
        )
        self.custom_cancel_button.setText(self.tr("Previous Step"))
        self.screenshot.updateUi()
        self.screenshot.set_title(
            self.tr("2. Import in each hardware signer")
            if self.num_keystores() > 1
            else self.tr("2. Import in the hardware signer")
        )


class DistributeSeeds(BaseTab):
    def create(self) -> TutorialWidget:

        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        if self.num_keystores() > 1:
            add_centered_icons(
                ["distribute-multisigsig-export.svgz"],
                widget,
                max_sizes=[(400, 350)] * self.num_keystores(),
            )
        else:
            add_centered_icons(
                ["distribute-singlesig-export.svgz"],
                widget,
                max_sizes=[(400, 350)] * self.num_keystores(),
            )

        widget.layout().itemAt(0).widget().setMaximumWidth(400)

        right_widget = QWidget()
        right_widget.setLayout(QVBoxLayout())
        right_widget.layout().setAlignment(Qt.AlignmentFlag.AlignVCenter)
        widget.layout().addWidget(right_widget)

        self.label_main = QLabel(widget)
        self.label_main.setWordWrap(True)

        right_widget.layout().addWidget(self.label_main)

        right_widget.layout().addItem(QSpacerItem(1, 40))
        self.buttonbox_buttons[0].setIcon(QIcon(icon_path("checkmark.png")))

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, self.buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.synchronize_visiblity(self.refs.wallet_tabs, set_also_visible=False)
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=False)

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()

        if self.num_keystores() > 1:
            self.label_main.setText(
                html_f(
                    f"""<ul>
  <li>{self.tr('Place each seed backup and hardware signer in a secure location, such:')}</li>
   <ul>
   {''.join([f"<li>{self.tr('Seed backup {j} and hardware signer {j} should be in location {j}').format(j=i+1)}</li>" for i in  range(self.num_keystores())]) if self.num_keystores()>1 else ""}
   </ul>   
   <li>{self.tr('Choose the secure places carefully, considering that you need to go to {m} of the {n}, to spend from your multisig-wallet.').format(m=self.refs.qtwalletbase.get_mn_tuple()[0], n=self.num_keystores())}</li>
</ul> """,
                    add_html_and_body=True,
                    p=True,
                    size=12,
                )
            )
        else:
            self.label_main.setText(
                html_f(
                    f"""<ul>
  <li>{self.tr('Store the  seed backup   in a <b>very</b> secure location (like a vault).')}</li>
   <ul>
      <li>{self.tr('The seed backup (24 words) give total control over the funds.')}</li>
   </ul>     
  <li>{self.tr('Store the   hardware signer   in secure location.')}</li>
   </ul>  
</ul>""",
                    add_html_and_body=True,
                    p=True,
                    size=12,
                )
            )
        self.buttonbox_buttons[0].setText(self.tr("Finish"))


class SendTest(BaseTab):
    def __init__(self, test_label, test_number, tx_text, refs: TabInfo) -> None:
        super().__init__(refs)
        self.test_label = test_label
        self.test_number = test_number
        self.tx_text = tx_text

    def create(self) -> TutorialWidget:

        widget = QWidget()
        widget.setLayout(QHBoxLayout())
        widget.layout().setContentsMargins(0, 0, 0, 0)  # Left, Top, Right, Bottom margins

        add_centered_icons(["send.svg"], widget, max_sizes=[(50, 80)])
        widget.layout().itemAt(0).widget().setMaximumWidth(150)

        inner_widget = QWidget()
        inner_widget_layout = QVBoxLayout(inner_widget)
        self.label = QLabel()
        if self.num_keystores() == 1:

            inner_widget_layout.addWidget(self.label)

        else:

            self.label = QLabel(html_f(self.tx_text, add_html_and_body=True, p=True, size=12))
            inner_widget_layout.addWidget(self.label)

        widget.layout().addWidget(inner_widget)

        tutorial_widget = TutorialWidget(
            self.refs.container, widget, self.buttonbox, buttonbox_always_visible=False
        )
        tutorial_widget.setMinimumHeight(30)

        def callback():
            if not self.refs.qt_wallet:
                return
            logger.debug(f"tutorial callback")

            # compare how many tx were already done , to the current test_number
            def should_offer_skip() -> bool:
                if not spend_txos:
                    return False
                return len(spend_txos) >= self.test_number + 1

            # offer to skip this step if it was spend from this wallet
            txos = self.refs.qt_wallet.wallet.get_all_txos(include_not_mine=False)
            spend_txos = [txo for txo in txos if txo.is_spent_by_txid]

            if not should_offer_skip():
                return

            if question_dialog(
                text=self.tr(
                    "You made {n} outgoing transactions already. Would you like to skip this spend test?"
                ).format(n=len(spend_txos)),
                title=self.tr("Skip spend test?"),
                buttons=QMessageBox.StandardButton.No | QMessageBox.StandardButton.Yes,
            ):
                self.refs.go_to_next_index()
                return

            self.refs.floating_button_box.fill_tx()

        tutorial_widget.set_callback(callback)
        tutorial_widget.synchronize_visiblity(
            self.refs.wallet_tabs, set_also_visible=bool(self.refs.qt_wallet)
        )
        tutorial_widget.synchronize_visiblity(self.refs.floating_button_box, set_also_visible=True)
        tutorial_widget.synchronize_visiblity(tutorial_widget.button_box, set_also_visible=False)
        if self.refs.qt_wallet:
            tutorial_widget.synchronize_visiblity(
                self.refs.qt_wallet.uitx_creator.button_box, set_also_visible=False
            )

        self.updateUi()
        return tutorial_widget

    def updateUi(self):
        super().updateUi()

        self.label.setText(
            html_f(
                self.tr("Complete the send test to ensure the hardware signer works!"),
                add_html_and_body=True,
                p=True,
                size=12,
            )
        )


class WalletSteps(StepProgressContainer):
    signal_create_wallet = pyqtSignal()

    def __init__(
        self,
        qtwalletbase: QtWalletBase,
        wallet_tabs: QTabWidget,
        max_test_fund=1_000_000,
        qt_wallet: QTWallet = None,
    ) -> None:
        super().__init__(step_labels=[""] * 3)  # initialize with 3 steps (doesnt matter)
        self.qtwalletbase = qtwalletbase
        self.qt_wallet = qt_wallet
        m, n = self.qtwalletbase.get_mn_tuple()

        # floating_button_box
        self.floating_button_box = FloatingButtonBar(
            self.fill_tx,
            self.qt_wallet.uitx_creator.create_tx
            if self.qt_wallet
            else lambda: Message(self.tr("You must have an initilized wallet first")),
            self.go_to_next_index,
            self.go_to_previous_index,
            self.qtwalletbase.signals,
        )
        self.floating_button_box.fill()
        self.qtwalletbase.outer_layout.addWidget(self.floating_button_box)

        refs = TabInfo(
            container=self,
            wallet_tabs=wallet_tabs,
            qtwalletbase=qtwalletbase,
            go_to_next_index=self.go_to_next_index,
            go_to_previous_index=self.go_to_previous_index,
            floating_button_box=self.floating_button_box,
            signal_create_wallet=self.signal_create_wallet,
            qt_wallet=self.qt_wallet,
            max_test_fund=max_test_fund,
        )

        self.tab_generators: Dict[TutorialStep, BaseTab] = {
            TutorialStep.buy: BuyHardware(refs=refs),
            TutorialStep.generate: GenerateSeed(refs=refs),
            TutorialStep.import_xpub: ImportXpubs(refs=refs),
            TutorialStep.backup_seed: BackupSeed(refs=refs),
            TutorialStep.validate_backup: ValidateBackup(refs=refs),
            TutorialStep.receive: ReceiveTest(refs=refs),
        }
        if n > 1:
            self.tab_generators[TutorialStep.register] = RegisterMultisig(refs=refs)

        for test_number, tutoral_step in enumerate(self.get_send_tests_steps()):
            self.tab_generators[tutoral_step] = SendTest(
                self.get_send_test_labels()[test_number],
                test_number=test_number,
                tx_text=self.tx_text(test_number),
                refs=refs,
            )

        self.tab_generators[TutorialStep.distribute] = DistributeSeeds(refs=refs)

        self.wallet_tabs = wallet_tabs
        self.max_test_fund = max_test_fund

        self.qtwalletbase.outer_layout.insertWidget(0, self)

        self.widgets: Dict[TutorialStep, TutorialWidget] = {
            key: generator.create() for key, generator in self.tab_generators.items()
        }

        # set_custom_widget  from StepProgressContainer
        for i, widget in enumerate(self.widgets.values()):
            self.set_custom_widget(i, widget)

        if self.qt_wallet:
            step = (
                self.count() - 1
                if self.qt_wallet.wallet.tutorial_index is None
                else self.qt_wallet.wallet.tutorial_index
            )
            self.set_current_index(step)

        self.updateUi()
        self.set_visibilities()

        self.qtwalletbase.signals.language_switch.connect(self.updateUi)

    def current_step(self) -> TutorialStep:
        return self.get_step_of_index(self.current_index())

    def index_of_step(self, step: TutorialStep) -> int:
        return list(TutorialStep).index(step)

    def get_step_of_index(self, index: int) -> TutorialStep:
        members = list(self.tab_generators.keys())
        if index < 0:
            index = 0
        if index >= len(members):
            index = len(members) - 1
        return members[index]

    def get_wallet_tutorial_index(self):
        return (
            (self.qt_wallet.wallet.tutorial_index)
            if self.qt_wallet
            else self.qtwalletbase.get_editable_protowallet().tutorial_index
        )

    def set_wallet_tutorial_index(self, value: Optional[int]):
        if self.qt_wallet:
            self.qt_wallet.wallet.tutorial_index = value
        else:
            self.qtwalletbase.get_editable_protowallet().tutorial_index = value

    def set_visibilities(self):
        should_be_visible = self.get_wallet_tutorial_index() != None
        self.setVisible(should_be_visible)

        if should_be_visible:
            self.signal_widget_focus.emit(self.widgets[self.current_step()])
        else:
            self.wallet_tabs.setVisible(True)
            self.floating_button_box.setVisible(False)
            if self.qt_wallet:
                self.qt_wallet.uitx_creator.button_box.setVisible(True)

    def num_keystores(self) -> int:
        return self.qtwalletbase.get_mn_tuple()[1]

    def change_index(self, index: int):
        self.set_current_index(index)

    def go_to_previous_index(self):
        self.change_index(max(self.current_index() - 1, 0))

    def go_to_next_index(self):
        if self.step_bar.current_index + 1 >= self.step_bar.number_of_steps:
            self.set_wallet_tutorial_index(None)
            self.set_visibilities()

            return
        self.change_index(min(self.step_bar.current_index + 1, self.step_bar.number_of_steps - 1))

    def get_send_tests_steps(self) -> List[TutorialStep]:
        m, n = self.qtwalletbase.get_mn_tuple()

        number = ceil(n / m)

        start_index = self.index_of_step(TutorialStep.send)

        return list(TutorialStep)[start_index : start_index + number]

    def get_send_test_labels(self) -> List[str]:
        m, n = self.qtwalletbase.get_mn_tuple()
        keystore_labels = self.qtwalletbase.get_keystore_labels()

        send_test_labels = []
        for i_send_tests, tutorial_step in enumerate(self.get_send_tests_steps()):

            start_signer = m * i_send_tests
            end_signer = min(m * i_send_tests + m, n)

            missing_signers = m - (end_signer - start_signer)
            start_signer -= missing_signers

            labels = [keystore_labels[j] for j in range(start_signer, end_signer)]
            send_test_labels.append(self.tr(" and ").join([f'"{label}"' for label in labels]))

        return send_test_labels

    def tx_text(self, test_number: int) -> str:
        if self.num_keystores() == 1:
            return self.tr("Send Test")
        else:

            return self.tr("Sign with {label}").format(label=self.get_send_test_labels()[test_number])

    def open_tx(self, test_number: int):
        if not self.qt_wallet:
            return

        label = self.tx_text(test_number)

        utxos = self.qt_wallet.wallet.bdkwallet.list_unspent()
        if not utxos:
            Message(self.tr("The wallet is not funded. Please fund the wallet."))
            return

        txinfos = TxUiInfos()
        txinfos.main_wallet_id = self.qt_wallet.wallet.id
        # inputs
        txinfos.fill_utxo_dict_from_utxos(utxos)
        # outputs
        txinfos.recipients.append(
            Recipient(
                self.qt_wallet.wallet.get_address().address.as_string(),
                0,
                checked_max_amount=True,
                label=label,
            )
        )

        self.qtwalletbase.signals.open_tx_like.emit(txinfos)

    def fill_tx(self):
        if not self.qt_wallet:
            return

        self.wallet_tabs.setCurrentWidget(self.qt_wallet.send_tab)

        # use the current_step, but if I highlighted another signing
        if self.current_step() in self.get_send_tests_steps():
            test_number = self.get_send_tests_steps().index(self.current_step())
        else:
            hightligted_step = self.get_step_of_index(self.current_highlighted_index())
            if hightligted_step in self.get_send_tests_steps():
                test_number = self.get_send_tests_steps().index(hightligted_step)
            else:
                # don't know what tx to fill
                return

        # set all tabs except the send as hidden
        for i in range(self.qt_wallet.tabs.count()):
            tab_widget = self.qt_wallet.tabs.widget(i)
            tab_widget.setHidden(tab_widget != self.qt_wallet.send_tab)
        self.open_tx(test_number)

    def updateUi(self):
        # step_bar
        labels: Dict[TutorialStep, str] = {
            TutorialStep.buy: self.tr("Turn on hardware signer"),
            TutorialStep.generate: self.tr("Generate Seed"),
            TutorialStep.import_xpub: self.tr("Import signer info"),
            TutorialStep.backup_seed: self.tr("Backup Seed"),
            TutorialStep.validate_backup: self.tr("Validate Backup"),
            TutorialStep.receive: self.tr("Receive Test"),
            TutorialStep.distribute: self.tr("Put in secure locations"),
            TutorialStep.register: self.tr("Register multisig on signers"),
        }
        for i, tutoral_step in enumerate(self.get_send_tests_steps()):
            labels[tutoral_step] = (
                self.tr("Send test {j}").format(j=i + 1)
                if len(self.get_send_tests_steps()) > 1
                else self.tr("Send test")
            )

        self.set_labels([labels[key] for key in self.tab_generators])
