import storage
import storage.device
import storage.recovery
from trezor import config, ui, wire, workflow
from trezor.messages import ButtonRequestType
from trezor.messages.Success import Success
from trezor.pin import pin_to_int
from trezor.ui.text import Text

from apps.common.confirm import require_confirm
from apps.common.request_pin import (
    request_pin_and_sd_salt,
    request_pin_confirm,
    show_pin_invalid,
)
from apps.management.recovery_device.homescreen import (
    recovery_homescreen,
    recovery_process,
)

if False:
    from trezor.messages.RecoveryDevice import RecoveryDevice


# List of RecoveryDevice fields that can be set when doing dry-run recovery.
# All except `dry_run` are allowed for T1 compatibility, but their values are ignored.
# If set, `enforce_wordlist` must be True, because we do not support non-enforcing.
DRY_RUN_ALLOWED_FIELDS = ("dry_run", "word_count", "enforce_wordlist", "type")


async def recovery_device(ctx: wire.Context, msg: RecoveryDevice) -> Success:
    """
    Recover BIP39/SLIP39 seed into empty device.
    Recovery is also possible with replugged Trezor. We call this process Persistance.
    User starts the process here using the RecoveryDevice msg and then they can unplug
    the device anytime and continue without a computer.
    """
    _validate(msg)

    if storage.recovery.is_in_progress():
        return await recovery_process(ctx)

    await _continue_dialog(ctx, msg)

    # for dry run pin needs to be entered
    if msg.dry_run:
        curpin, salt = await request_pin_and_sd_salt(ctx, "Enter PIN")
        if not config.check_pin(pin_to_int(curpin), salt):
            await show_pin_invalid(ctx)
            raise wire.PinInvalid("PIN invalid")

    if not msg.dry_run:
        # set up pin if requested
        if msg.pin_protection:
            newpin = await request_pin_confirm(ctx, allow_cancel=False)
            config.change_pin(pin_to_int(""), pin_to_int(newpin), None, None)

        if msg.u2f_counter is not None:
            storage.device.set_u2f_counter(msg.u2f_counter)
        storage.device.load_settings(
            label=msg.label, use_passphrase=msg.passphrase_protection
        )

    storage.recovery.set_in_progress(True)
    storage.recovery.set_dry_run(bool(msg.dry_run))

    workflow.replace_default(recovery_homescreen)
    return await recovery_process(ctx)


def _validate(msg: RecoveryDevice) -> None:
    if not msg.dry_run and storage.is_initialized():
        raise wire.UnexpectedMessage("Already initialized")
    if msg.dry_run and not storage.is_initialized():
        raise wire.NotInitialized("Device is not initialized")

    if msg.enforce_wordlist is False:
        raise wire.ProcessError(
            "Value enforce_wordlist must be True, Trezor Core enforces words automatically."
        )

    if msg.dry_run:
        # check that only allowed fields are set
        for key, value in msg.__dict__.items():
            if key not in DRY_RUN_ALLOWED_FIELDS and value is not None:
                raise wire.ProcessError(
                    "Forbidden field set in dry-run: {}".format(key)
                )


async def _continue_dialog(ctx: wire.Context, msg: RecoveryDevice) -> None:
    if not msg.dry_run:
        text = Text("Recovery mode", ui.ICON_RECOVERY, new_lines=False)
        text.bold("Do you really want to")
        text.br()
        text.bold("recover a wallet?")

        text.br()
        text.br_half()
        text.normal("By continuing you agree")
        text.br()
        text.normal("to")
        text.bold("https://trezor.io/tos")
    else:
        text = Text("Seed check", ui.ICON_RECOVERY, new_lines=False)
        text.normal("Do you really want to", "check the recovery", "seed?")
    await require_confirm(ctx, text, code=ButtonRequestType.ProtectCall)
