"""Shared test fixtures."""

import json
import os

import mm_web3
import pytest
from dotenv import load_dotenv
from eth_typing import ABI
from mm_web3 import fetch_proxies_sync
from typer.testing import CliRunner

from mm_eth.anvil import Anvil

load_dotenv()


@pytest.fixture
def mainnet() -> str:
    """Mainnet HTTP RPC URL."""
    res = os.getenv("MAINNET_RPC")
    if not res:
        raise RuntimeError("MAINNET_RPC environment variable is not set")
    return res


@pytest.fixture
def mainnet_ws() -> str:
    """Mainnet WebSocket RPC URL."""
    res = os.getenv("MAINNET_RPC_WS")
    if not res:
        raise RuntimeError("MAINNET_RPC_WS environment variable is not set")
    return res


@pytest.fixture
def address_bnb():
    """BNB token contract address."""
    return "0xB8c77482e45F1F44dE1745F52C74426C631bDD52"


@pytest.fixture
def address_tether():
    """Tether token contract address."""
    return "0xdac17f958d2ee523a2206206994597c13d831ec7"


@pytest.fixture
def address_binance():
    """Binance hot wallet address."""
    return "0xBE0eB53F46cd790Cd13851d5EFf43D12404d33E8"


@pytest.fixture()
def mnemonic() -> str:
    """Test mnemonic phrase."""
    return "diet render mix evil relax apology hazard bamboo desert sign fence usage baby athlete cannon season busy ten jaguar silk rebel identify foster shrimp"  # noqa: E501


@pytest.fixture
def address_0():
    """Test address derived at index 0."""
    return "0x10fd602Bff689e64D4720D1DCCCD3494f1f16623"


@pytest.fixture
def private_0():
    """Test private key at index 0."""
    return "0x7bb5b9c0ba991275f84b796b4d25fd3a8d7320911f50fade85410e7a2b000632"


@pytest.fixture
def address_1():
    """Test address derived at index 1."""
    return "0x58487485c3858109f5A37e42546FE87473f79a4b"


@pytest.fixture
def private_1():
    """Test private key at index 1."""
    return "0xe4d16faffffa9b28adf02fb5f06998d174046c369d2daffe9a750fbe6a333417"


@pytest.fixture
def address_2():
    """Test address derived at index 2."""
    return "0x97C77B548aE0d4925F5C201220fC6d8996424309"


@pytest.fixture
def private_2():
    """Test private key at index 2."""
    return "0xb7e0b671e176b04ceb0897a698d34771bfe9acf29273dc52a141be6e97145a00"


@pytest.fixture()
def erc20_token_abi_str() -> str:
    """ERC20 token ABI as a JSON string."""
    return r'[{"inputs":[{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"totalSupply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]'  # noqa: E501


@pytest.fixture()
def erc20_abi(erc20_token_abi_str) -> ABI:
    """Parse ERC20 token ABI from JSON string."""
    return json.loads(erc20_token_abi_str)


@pytest.fixture()
def erc20_token_bin() -> str:
    """ERC20 token bytecode."""
    return " "  # noqa: E501


@pytest.fixture(scope="session")
def proxies() -> list[str]:
    """Proxy list fetched from PROXIES_URL."""
    proxies_url = os.getenv("PROXIES_URL")
    if not proxies_url:
        raise RuntimeError("PROXIES_URL environment variable is not set")
    return fetch_proxies_sync(proxies_url).unwrap()


@pytest.fixture()
def random_proxy(proxies: list[str]) -> str:
    """Random proxy from the proxy list."""
    res = mm_web3.random_proxy(proxies)
    if not res:
        raise RuntimeError("No proxies available")
    return res


@pytest.fixture
def cli_runner() -> CliRunner:
    """Typer CLI test runner."""
    return CliRunner()


@pytest.fixture()
async def anvil(mnemonic):
    """Launch an Anvil local node instance."""
    res = await Anvil.launch(mnemonic=mnemonic)
    if res.is_err():
        raise RuntimeError(f"can't start anvil: {res.unwrap_err()}")

    a = res.unwrap()
    try:
        yield a
    finally:
        a.stop()
