import fnmatch
import os
import pytest
import time
from configparser import ConfigParser
from functools import partial
from pathlib import Path
from ragger.backend import BackendInterface
from ragger.firmware import Firmware
from ragger.navigator import Navigator, NavInsID
import json

import ledger_app_clients.ethereum.response_parser as ResponseParser
from ledger_app_clients.ethereum.client import EthAppClient
from ledger_app_clients.ethereum.eip712 import InputData
from ledger_app_clients.ethereum.settings import SettingID, settings_toggle


BIP32_PATH = "m/44'/60'/0'/0/0"


def input_files() -> list[str]:
    files = []
    for file in os.scandir("%s/eip712_input_files" % (os.path.dirname(__file__))):
        if fnmatch.fnmatch(file, "*-data.json"):
            files.append(file.path)
    return sorted(files)


@pytest.fixture(params=input_files())
def input_file(request) -> str:
    return Path(request.param)


@pytest.fixture(params=[True, False])
def verbose(request) -> bool:
    return request.param


@pytest.fixture(params=[False, True])
def filtering(request) -> bool:
    return request.param


def test_eip712_legacy(firmware: Firmware,
                       backend: BackendInterface,
                       navigator: Navigator):
    app_client = EthAppClient(backend)
    with app_client.eip712_sign_legacy(
            BIP32_PATH,
            bytes.fromhex('6137beb405d9ff777172aa879e33edb34a1460e701802746c5ef96e741710e59'),
            bytes.fromhex('eb4221181ff3f1a83ea7313993ca9218496e424604ba9492bb4052c03d5c3df8')):
        moves = list()
        if firmware.device.startswith("nano"):
            moves += [NavInsID.RIGHT_CLICK]
            if firmware.device == "nanos":
                screens_per_hash = 4
            else:
                screens_per_hash = 2
            moves += [NavInsID.RIGHT_CLICK] * screens_per_hash * 2
            moves += [NavInsID.BOTH_CLICK]
        else:
            moves += [NavInsID.USE_CASE_REVIEW_TAP] * 2
            moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
        navigator.navigate(moves)

    v, r, s = ResponseParser.signature(app_client.response().data)

    assert v == bytes.fromhex("1c")
    assert r == bytes.fromhex("ea66f747173762715751c889fea8722acac3fc35db2c226d37a2e58815398f64")
    assert s == bytes.fromhex("52d8ba9153de9255da220ffd36762c0b027701a3b5110f0a765f94b16a9dfb55")


def autonext(fw: Firmware, nav: Navigator):
    moves = list()
    if fw.device.startswith("nano"):
        moves = [NavInsID.RIGHT_CLICK]
    else:
        moves = [NavInsID.USE_CASE_REVIEW_TAP]
    nav.navigate(moves, screen_change_before_first_instruction=False, screen_change_after_last_instruction=False)


def test_eip712_new(firmware: Firmware,
                    backend: BackendInterface,
                    navigator: Navigator,
                    input_file: Path,
                    verbose: bool,
                    filtering: bool):
    app_client = EthAppClient(backend)
    if firmware.device == "nanos":
        pytest.skip("Not supported on LNS")
    else:
        test_path = "%s/%s" % (input_file.parent, "-".join(input_file.stem.split("-")[:-1]))
        conf_file = "%s.ini" % (test_path)

        filters = None
        if filtering:
            try:
                with open("%s-filter.json" % (test_path)) as f:
                    filters = json.load(f)
            except (IOError, json.decoder.JSONDecodeError) as e:
                pytest.skip("Filter file error: %s" % (e.strerror))

        config = ConfigParser()
        config.read(conf_file)

        # sanity check
        assert "signature" in config.sections()
        assert "v" in config["signature"]
        assert "r" in config["signature"]
        assert "s" in config["signature"]

        if verbose:
            settings_toggle(firmware, navigator, [SettingID.VERBOSE_EIP712])

        with open(input_file) as file:
            assert InputData.process_data(app_client,
                                          json.load(file),
                                          filters,
                                          partial(autonext, firmware, navigator))
            with app_client.eip712_sign_new(BIP32_PATH):
                # tight on timing, needed by the CI otherwise might fail sometimes
                time.sleep(0.5)

                moves = list()
                if firmware.device.startswith("nano"):
                    if not verbose and not filtering:  # need to skip the message hash
                        moves = [NavInsID.RIGHT_CLICK] * 2
                    moves += [NavInsID.BOTH_CLICK]
                else:
                    if not verbose and not filtering:  # need to skip the message hash
                        moves += [NavInsID.USE_CASE_REVIEW_TAP]
                    moves += [NavInsID.USE_CASE_REVIEW_CONFIRM]
                navigator.navigate(moves)
            v, r, s = ResponseParser.signature(app_client.response().data)

        assert v == bytes.fromhex(config["signature"]["v"])
        assert r == bytes.fromhex(config["signature"]["r"])
        assert s == bytes.fromhex(config["signature"]["s"])
