import abc
import numpy as np
import pandas as pd

import typing


class AutomlBaseClass(abc.ABC):

    def fit(self, meta_data: pd.DataFrame,
            aggregate: typing.Optional[typing.Callable]) -> typing.List:
        """
        Takes a pandas data frame containing meta data as input, where each row represents a configuration, each column
        represents a dataset and each cell represents the performance of that configuration on that dataset. The rows
        are indexed by the actual hyperparameter settings

        :param meta_data: the dataframe
        :param aggregate: the aggregation function (will only be used for greedy defaults, ignored for average rank)
        :return: a list of configurations
        """
        raise NotImplementedError('Interface method, please subclass!')

    @staticmethod
    def evaluate(defaults: typing.List, test_data: pd.Series) -> float:
        """
        Takes the defaults as generated by the fit method, and evaluates them on a specific test task. The test task
        is a pandas series, where each index is the configuration, and each value is the performance of that
        configuration on the test task.

        Make sure to slice the defaults to only contain the subset of interest (typically not all defaults).

        :param defaults: the defaults, as generated by the fit method
        :param test_data: the series
        :return: the best obtained performance
        """
        return max(test_data[defaults])


class AverageRank(AutomlBaseClass):

    def fit(self, meta_data: pd.DataFrame,
            aggregate: typing.Optional[typing.Callable]) -> typing.List:
        raise NotImplementedError()


class GreedyDefaults(AutomlBaseClass):

    def fit(self, meta_data: pd.DataFrame, aggregate: typing.Callable) -> typing.List:
        raise NotImplementedError()
