from pathlib import Path
from unittest.mock import MagicMock

import pytest
import rapidjson

from freqtrade.commands.build_config_commands import start_new_config
from freqtrade.configuration.deploy_config import (
    ask_user_config,
    ask_user_overwrite,
    validate_is_float,
    validate_is_int,
)
from freqtrade.exceptions import OperationalException
from tests.conftest import get_args, log_has_re


def test_validate_is_float():
    assert validate_is_float("2.0")
    assert validate_is_float("2.1")
    assert validate_is_float("0.1")
    assert validate_is_float("-0.5")
    assert not validate_is_float("-0.5e")


def test_validate_is_int():
    assert validate_is_int("2")
    assert validate_is_int("6")
    assert validate_is_int("-1")
    assert validate_is_int("500")
    assert not validate_is_int("2.0")
    assert not validate_is_int("2.1")
    assert not validate_is_int("-2.1")
    assert not validate_is_int("-ee")


@pytest.mark.parametrize("exchange", ["bybit", "binance", "kraken"])
def test_start_new_config(mocker, caplog, exchange):
    wt_mock = mocker.patch.object(Path, "write_text", MagicMock())
    mocker.patch.object(Path, "exists", MagicMock(return_value=True))
    unlink_mock = mocker.patch.object(Path, "unlink", MagicMock())
    mocker.patch("freqtrade.configuration.deploy_config.ask_user_overwrite", return_value=True)

    sample_selections = {
        "max_open_trades": 3,
        "stake_currency": "USDT",
        "stake_amount": 100,
        "fiat_display_currency": "EUR",
        "timeframe": "15m",
        "dry_run": True,
        "trading_mode": "spot",
        "margin_mode": "",
        "exchange_name": exchange,
        "exchange_key": "sampleKey",
        "exchange_secret": "Samplesecret",
        "telegram": False,
        "telegram_token": "asdf1244",
        "telegram_chat_id": "1144444",
        "api_server": False,
        "api_server_listen_addr": "127.0.0.1",
        "api_server_username": "freqtrader",
        "api_server_password": "MoneyMachine",
    }
    mocker.patch(
        "freqtrade.configuration.deploy_config.ask_user_config", return_value=sample_selections
    )
    args = ["new-config", "--config", "coolconfig.json"]
    start_new_config(get_args(args))

    assert log_has_re("Writing config to .*", caplog)
    assert wt_mock.call_count == 1
    assert unlink_mock.call_count == 1
    result = rapidjson.loads(
        wt_mock.call_args_list[0][0][0],
        parse_mode=rapidjson.PM_COMMENTS | rapidjson.PM_TRAILING_COMMAS,
    )
    assert result["exchange"]["name"] == exchange
    assert result["timeframe"] == "15m"


def test_start_new_config_exists(mocker, caplog):
    mocker.patch.object(Path, "exists", MagicMock(return_value=True))
    mocker.patch("freqtrade.configuration.deploy_config.ask_user_overwrite", return_value=False)
    args = ["new-config", "--config", "coolconfig.json"]
    with pytest.raises(OperationalException, match=r"Configuration .* already exists\."):
        start_new_config(get_args(args))


def test_ask_user_overwrite(mocker):
    """
    Once https://github.com/tmbo/questionary/issues/35 is implemented, improve this test.
    """
    prompt_mock = mocker.patch(
        "freqtrade.configuration.deploy_config.prompt", return_value={"overwrite": False}
    )
    assert not ask_user_overwrite(Path("test.json"))
    assert prompt_mock.call_count == 1

    prompt_mock.reset_mock()
    prompt_mock = mocker.patch(
        "freqtrade.configuration.deploy_config.prompt", return_value={"overwrite": True}
    )
    assert ask_user_overwrite(Path("test.json"))
    assert prompt_mock.call_count == 1


def test_ask_user_config(mocker):
    """
    Once https://github.com/tmbo/questionary/issues/35 is implemented, improve this test.
    """
    prompt_mock = mocker.patch(
        "freqtrade.configuration.deploy_config.prompt", return_value={"overwrite": False}
    )
    answers = ask_user_config()
    assert isinstance(answers, dict)
    assert prompt_mock.call_count == 1

    prompt_mock = mocker.patch("freqtrade.configuration.deploy_config.prompt", return_value={})

    with pytest.raises(OperationalException, match=r"User interrupted interactive questions\."):
        ask_user_config()
