# This file is part of the Trezor project.
#
# Copyright (C) 2012-2019 SatoshiLabs and contributors
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version 3
# as published by the Free Software Foundation.
#
# This library 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 Lesser General Public License for more details.
#
# You should have received a copy of the License along with this library.
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.

from typing import Iterator

import pytest

from trezorlib import device
from trezorlib.debuglink import DebugLink

from .. import buttons
from ..click_tests import recovery
from ..common import MNEMONIC_SLIP39_ADVANCED_20, MNEMONIC_SLIP39_BASIC_20_3of6
from ..device_handler import BackgroundDeviceHandler
from ..emulators import Emulator, EmulatorWrapper
from ..upgrade_tests import core_only


@pytest.fixture
def emulator() -> Iterator[Emulator]:
    with EmulatorWrapper("core") as emu:
        yield emu


def _restart(device_handler: BackgroundDeviceHandler, emulator: Emulator):
    device_handler.restart(emulator)
    return device_handler.debuglink()


@core_only
def test_abort(emulator: Emulator):
    device_handler = BackgroundDeviceHandler(emulator.client)
    debug = device_handler.debuglink()
    features = device_handler.features()

    assert features.recovery_mode is False

    device_handler.run(device.recover, pin_protection=False)
    layout = debug.wait_layout()
    assert layout.get_title() == "WALLET RECOVERY"

    layout = debug.click(buttons.OK, wait=True)
    assert "Select number of words" in layout.get_content()

    device_handler.restart(emulator)
    debug = device_handler.debuglink()
    features = device_handler.features()

    assert features.recovery_mode is True

    # no waiting for layout because layout doesn't change
    layout = debug.read_layout()
    assert "Select number of words" in layout.get_content()
    layout = debug.click(buttons.CANCEL, wait=True)

    assert layout.get_title() == "ABORT RECOVERY"
    layout = debug.click(buttons.OK, wait=True)

    assert layout.text.startswith("< Homescreen")
    features = device_handler.features()
    assert features.recovery_mode is False


@core_only
def test_recovery_single_reset(emulator: Emulator):
    device_handler = BackgroundDeviceHandler(emulator.client)
    debug = device_handler.debuglink()
    features = device_handler.features()

    assert features.initialized is False
    assert features.recovery_mode is False

    device_handler.run(device.recover, pin_protection=False)
    recovery.confirm_recovery(debug)

    recovery.select_number_of_words(debug)

    debug = _restart(device_handler, emulator)
    features = device_handler.features()
    assert features.recovery_mode is True

    # we need to enter the number of words again, that's a feature
    recovery.select_number_of_words(debug)
    recovery.enter_shares(debug, MNEMONIC_SLIP39_BASIC_20_3of6)
    recovery.finalize(debug)

    features = device_handler.features()
    assert features.initialized is True
    assert features.recovery_mode is False


@core_only
def test_recovery_on_old_wallet(emulator: Emulator):
    """Check that the recovery workflow started on a disconnected device can survive
    handling by the old Wallet.

    While Suite will send a RecoveryDevice message and hook into the running recovery
    flow, old Wallet can't do that and instead must repeatedly ask for features (via
    Initialize+GetFeatures). At minimum, these two messages must not interrupt the
    running recovery.
    """
    device_handler = BackgroundDeviceHandler(emulator.client)
    debug = device_handler.debuglink()
    features = device_handler.features()

    assert features.initialized is False
    assert features.recovery_mode is False

    # enter recovery mode
    device_handler.run(device.recover, pin_protection=False)
    recovery.confirm_recovery(debug)

    # restart to get into stand-alone recovery
    debug = _restart(device_handler, emulator)
    features = device_handler.features()
    assert features.recovery_mode is True

    # enter number of words
    recovery.select_number_of_words(debug)

    first_share = MNEMONIC_SLIP39_BASIC_20_3of6[0]
    words = first_share.split(" ")

    # start entering first share
    layout = debug.read_layout()
    assert "Enter any share" in layout.text
    debug.press_yes()
    layout = debug.wait_layout()
    assert layout.text == "< MnemonicKeyboard >"

    # enter first word
    debug.input(words[0])
    layout = debug.wait_layout()

    # while keyboard is open, hit the device with Initialize/GetFeatures
    device_handler.client.init_device()
    device_handler.client.refresh_features()

    # try entering remaining 19 words
    for word in words[1:]:
        assert layout.text == "< MnemonicKeyboard >"
        debug.input(word)
        layout = debug.wait_layout()

    # check that we entered the first share successfully
    assert "2 more shares" in layout.text

    # try entering the remaining shares
    for share in MNEMONIC_SLIP39_BASIC_20_3of6[1:3]:
        recovery.enter_share(debug, share)

    recovery.finalize(debug)

    # check that the recovery succeeded
    features = device_handler.features()
    assert features.initialized is True
    assert features.recovery_mode is False


@core_only
def test_recovery_multiple_resets(emulator: Emulator):
    def enter_shares_with_restarts(debug: DebugLink) -> None:
        shares = MNEMONIC_SLIP39_ADVANCED_20
        layout = debug.read_layout()
        expected_text = "Enter any share"
        remaining = len(shares)
        for share in shares:
            assert expected_text in layout.text
            layout = recovery.enter_share(debug, share)
            remaining -= 1
            expected_text = "You have entered"
            debug = _restart(device_handler, emulator)

        assert "You have successfully recovered your wallet" in layout.get_content()

    device_handler = BackgroundDeviceHandler(emulator.client)
    debug = device_handler.debuglink()
    features = device_handler.features()

    assert features.initialized is False
    assert features.recovery_mode is False

    # start device and recovery
    device_handler.run(device.recover, pin_protection=False)
    recovery.confirm_recovery(debug)

    # set number of words
    recovery.select_number_of_words(debug)

    # restart
    debug = _restart(device_handler, emulator)
    features = device_handler.features()
    assert features.recovery_mode is True

    # enter the number of words again, that's a feature!
    recovery.select_number_of_words(debug)

    # enter shares and restart after each one
    enter_shares_with_restarts(debug)
    debug = device_handler.debuglink()
    layout = debug.read_layout()
    assert layout.text.startswith("< Homescreen")

    features = device_handler.features()
    assert features.initialized is True
    assert features.recovery_mode is False