# pragma pylint: disable=missing-docstring, protected-access, invalid-name

import re
import shutil
from pathlib import Path

from freqtrade.commands.strategy_utils_commands import start_strategy_update
from freqtrade.strategy.strategyupdater import StrategyUpdater
from tests.conftest import get_args


def test_strategy_updater_start(user_dir, capsys) -> None:
    # Effective test without mocks.
    teststrats = Path(__file__).parent / "strategy/strats"
    tmpdirp = Path(user_dir) / "strategies"
    tmpdirp.mkdir(parents=True, exist_ok=True)
    shutil.copy(teststrats / "strategy_test_v2.py", tmpdirp)
    old_code = (teststrats / "strategy_test_v2.py").read_text(encoding="utf-8")

    args = ["strategy-updater", "--userdir", str(user_dir), "--strategy-list", "StrategyTestV2"]
    pargs = get_args(args)
    pargs["config"] = None

    start_strategy_update(pargs)

    assert Path(user_dir / "strategies_orig_updater").exists()
    # Backup file exists
    assert Path(user_dir / "strategies_orig_updater" / "strategy_test_v2.py").exists()
    # updated file exists
    new_file = tmpdirp / "strategy_test_v2.py"
    assert new_file.exists()
    new_code = new_file.read_text(encoding="utf-8")
    assert "INTERFACE_VERSION = 3" in new_code
    assert "INTERFACE_VERSION = 2" in old_code
    captured = capsys.readouterr()

    assert "Conversion of strategy_test_v2.py started." in captured.out
    assert re.search(r"Conversion of strategy_test_v2\.py took .* seconds", captured.out)


def test_strategy_updater_methods(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()
    modified_code1 = instance_strategy_updater.update_code(
        """
import numpy as np
class testClass(IStrategy):
    def populate_buy_trend():
        some_variable = np.NaN
        pass
    def populate_sell_trend():
        pass
    def check_buy_timeout():
        pass
    def check_sell_timeout():
        pass
    def custom_sell():
        pass
"""
    )

    assert "populate_entry_trend" in modified_code1
    assert "populate_exit_trend" in modified_code1
    assert "check_entry_timeout" in modified_code1
    assert "check_exit_timeout" in modified_code1
    assert "custom_exit" in modified_code1
    assert "INTERFACE_VERSION = 3" in modified_code1
    assert "np.nan" in modified_code1


def test_strategy_updater_params(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()

    modified_code2 = instance_strategy_updater.update_code(
        """
ticker_interval = '15m'
buy_some_parameter = IntParameter(space='buy')
sell_some_parameter = IntParameter(space='sell')
"""
    )

    assert "timeframe" in modified_code2
    # check for not editing hyperopt spaces
    assert "space='buy'" in modified_code2
    assert "space='sell'" in modified_code2


def test_strategy_updater_constants(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()
    modified_code3 = instance_strategy_updater.update_code(
        """
use_sell_signal = True
sell_profit_only = True
sell_profit_offset = True
ignore_roi_if_buy_signal = True
forcebuy_enable = True
"""
    )

    assert "use_exit_signal" in modified_code3
    assert "exit_profit_only" in modified_code3
    assert "exit_profit_offset" in modified_code3
    assert "ignore_roi_if_entry_signal" in modified_code3
    assert "force_entry_enable" in modified_code3


def test_strategy_updater_df_columns(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()
    modified_code = instance_strategy_updater.update_code(
        """
dataframe.loc[reduce(lambda x, y: x & y, conditions), ["buy", "buy_tag"]] = (1, "buy_signal_1")
dataframe.loc[reduce(lambda x, y: x & y, conditions), 'sell'] = 1
"""
    )

    assert "enter_long" in modified_code
    assert "exit_long" in modified_code
    assert "enter_tag" in modified_code


def test_strategy_updater_method_params(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()
    modified_code = instance_strategy_updater.update_code(
        """
def confirm_trade_exit(sell_reason: str):
    nr_orders = trade.nr_of_successful_buys
    pass
    """
    )
    assert "exit_reason" in modified_code
    assert "nr_orders = trade.nr_of_successful_entries" in modified_code


def test_strategy_updater_dicts(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()
    modified_code = instance_strategy_updater.update_code(
        """
order_time_in_force = {
    'buy': 'gtc',
    'sell': 'ioc'
}
order_types = {
    'buy': 'limit',
    'sell': 'market',
    'stoploss': 'market',
    'stoploss_on_exchange': False
}
unfilledtimeout = {
    'buy': 1,
    'sell': 2
}
"""
    )

    assert "'entry': 'gtc'" in modified_code
    assert "'exit': 'ioc'" in modified_code
    assert "'entry': 'limit'" in modified_code
    assert "'exit': 'market'" in modified_code
    assert "'entry': 1" in modified_code
    assert "'exit': 2" in modified_code


def test_strategy_updater_comparisons(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()
    modified_code = instance_strategy_updater.update_code(
        """
def confirm_trade_exit(sell_reason):
    if (sell_reason == 'stop_loss'):
        pass
"""
    )
    assert "exit_reason" in modified_code
    assert "exit_reason == 'stop_loss'" in modified_code


def test_strategy_updater_strings(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()

    modified_code = instance_strategy_updater.update_code(
        """
sell_reason == 'sell_signal'
sell_reason == 'force_sell'
sell_reason == 'emergency_sell'
"""
    )

    # those tests currently don't work, next in line.
    assert "exit_signal" in modified_code
    assert "exit_reason" in modified_code
    assert "force_exit" in modified_code
    assert "emergency_exit" in modified_code


def test_strategy_updater_comments(default_conf, caplog) -> None:
    instance_strategy_updater = StrategyUpdater()
    modified_code = instance_strategy_updater.update_code(
        """
# This is the 1st comment
import talib.abstract as ta
# This is the 2nd comment
import freqtrade.vendor.qtpylib.indicators as qtpylib


class someStrategy(IStrategy):
    INTERFACE_VERSION = 2
    # This is the 3rd comment
    # This attribute will be overridden if the config file contains "minimal_roi"
    minimal_roi = {
        "0": 0.50
    }

    # This is the 4th comment
    stoploss = -0.1
"""
    )

    assert "This is the 1st comment" in modified_code
    assert "This is the 2nd comment" in modified_code
    assert "This is the 3rd comment" in modified_code
    assert "INTERFACE_VERSION = 3" in modified_code
    # currently still missing:
    # Webhook terminology, Telegram notification settings, Strategy/Config settings
