import logging
from typing import List

from freqtrade.exceptions import OperationalException


logger = logging.getLogger(__name__)


def hyperopt_filter_epochs(epochs: List, filteroptions: dict, log: bool = True) -> List:
    """
    Filter our items from the list of hyperopt results
    """
    if filteroptions["only_best"]:
        epochs = [x for x in epochs if x["is_best"]]
    if filteroptions["only_profitable"]:
        epochs = [x for x in epochs if x["results_metrics"].get("profit_total", 0) > 0]

    epochs = _hyperopt_filter_epochs_trade_count(epochs, filteroptions)

    epochs = _hyperopt_filter_epochs_duration(epochs, filteroptions)

    epochs = _hyperopt_filter_epochs_profit(epochs, filteroptions)

    epochs = _hyperopt_filter_epochs_objective(epochs, filteroptions)
    if log:
        logger.info(
            f"{len(epochs)} "
            + ("best " if filteroptions["only_best"] else "")
            + ("profitable " if filteroptions["only_profitable"] else "")
            + "epochs found."
        )
    return epochs


def _hyperopt_filter_epochs_trade(epochs: List, trade_count: int):
    """
    Filter epochs with trade-counts > trades
    """
    return [x for x in epochs if x["results_metrics"].get("total_trades", 0) > trade_count]


def _hyperopt_filter_epochs_trade_count(epochs: List, filteroptions: dict) -> List:
    if filteroptions["filter_min_trades"] > 0:
        epochs = _hyperopt_filter_epochs_trade(epochs, filteroptions["filter_min_trades"])

    if filteroptions["filter_max_trades"] > 0:
        epochs = [
            x
            for x in epochs
            if x["results_metrics"].get("total_trades") < filteroptions["filter_max_trades"]
        ]
    return epochs


def _hyperopt_filter_epochs_duration(epochs: List, filteroptions: dict) -> List:
    def get_duration_value(x):
        # Duration in minutes ...
        if "holding_avg_s" in x["results_metrics"]:
            avg = x["results_metrics"]["holding_avg_s"]
            return avg // 60
        raise OperationalException(
            "Holding-average not available. Please omit the filter on average time, "
            "or rerun hyperopt with this version"
        )

    if filteroptions["filter_min_avg_time"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)
        epochs = [x for x in epochs if get_duration_value(x) > filteroptions["filter_min_avg_time"]]
    if filteroptions["filter_max_avg_time"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)
        epochs = [x for x in epochs if get_duration_value(x) < filteroptions["filter_max_avg_time"]]

    return epochs


def _hyperopt_filter_epochs_profit(epochs: List, filteroptions: dict) -> List:
    if filteroptions["filter_min_avg_profit"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)
        epochs = [
            x
            for x in epochs
            if x["results_metrics"].get("profit_mean", 0) * 100
            > filteroptions["filter_min_avg_profit"]
        ]
    if filteroptions["filter_max_avg_profit"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)
        epochs = [
            x
            for x in epochs
            if x["results_metrics"].get("profit_mean", 0) * 100
            < filteroptions["filter_max_avg_profit"]
        ]
    if filteroptions["filter_min_total_profit"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)
        epochs = [
            x
            for x in epochs
            if x["results_metrics"].get("profit_total_abs", 0)
            > filteroptions["filter_min_total_profit"]
        ]
    if filteroptions["filter_max_total_profit"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)
        epochs = [
            x
            for x in epochs
            if x["results_metrics"].get("profit_total_abs", 0)
            < filteroptions["filter_max_total_profit"]
        ]
    return epochs


def _hyperopt_filter_epochs_objective(epochs: List, filteroptions: dict) -> List:
    if filteroptions["filter_min_objective"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)

        epochs = [x for x in epochs if x["loss"] < filteroptions["filter_min_objective"]]
    if filteroptions["filter_max_objective"] is not None:
        epochs = _hyperopt_filter_epochs_trade(epochs, 0)

        epochs = [x for x in epochs if x["loss"] > filteroptions["filter_max_objective"]]

    return epochs
