import numpy as np

from federatedml.feature.feature_selection.model_adapter import isometric_model
from federatedml.feature.feature_selection.model_adapter.adapter_base import BaseAdapter
from federatedml.util import consts


def feature_importance_converter(model_meta, model_param):

    # extract feature importance from model param
    fid_mapping = dict(model_param.feature_name_fid_mapping)
    feat_importance_list = list(model_param.feature_importances)
    fids = list(fid_mapping.keys())

    cols_names, importance_val = [], []

    for feat_importance in feat_importance_list:
        site_name = feat_importance.sitename
        fid = feat_importance.fid
        importance = feat_importance.importance
        feature_name = fid_mapping[fid]
        cols_names.append(feature_name)
        importance_val.append(importance)

    for fid in fids:
        if fid_mapping[fid] not in cols_names:
            cols_names.append(fid_mapping[fid])
            importance_val.append(0)

    single_info = isometric_model.SingleMetricInfo(
        values=np.array(importance_val),
        col_names=cols_names
    )
    result = isometric_model.IsometricModel()
    result.add_metric_value(metric_name=consts.FEATURE_IMPORTANCE, metric_info=single_info)

    return result


def feature_importance_with_anonymous_converter(model_meta, model_param):

    # extract feature importance from model param

    fid_mapping = dict(model_param.feature_name_fid_mapping)
    feat_importance_list = list(model_param.feature_importances)
    local_fids = list(fid_mapping.keys())
    local_cols, local_val = [], []

    # key is int party id, value is a dict, which has two key: col_name and value
    host_side_data = {}

    for feat_importance in feat_importance_list:
        fid = feat_importance.fid
        importance = feat_importance.importance
        site_name = feat_importance.sitename
        if site_name == consts.HOST_LOCAL:
            local_cols.append(fid_mapping[fid])
            local_val.append(importance)
        else:
            site_name = site_name.split(':')
            if site_name[0] == consts.HOST:
                continue
            else:
                local_cols.append(fid_mapping[fid])
                local_val.append(importance)

    for fid in local_fids:
        if fid_mapping[fid] not in local_cols:
            local_cols.append(fid_mapping[fid])
            local_val.append(0)

    single_info = isometric_model.SingleMetricInfo(
        values=np.array(local_val),
        col_names=local_cols
    )
    result = isometric_model.IsometricModel()
    result.add_metric_value(metric_name=consts.FEATURE_IMPORTANCE, metric_info=single_info)
    return result


class HomoSBTAdapter(BaseAdapter):

    def convert(self, model_meta, model_param):
        return feature_importance_converter(model_meta, model_param)


class HeteroSBTAdapter(BaseAdapter):

    def convert(self, model_meta, model_param):
        return feature_importance_with_anonymous_converter(model_meta, model_param)


class HeteroFastSBTAdapter(BaseAdapter):

    def convert(self, model_meta, model_param):
        model_name = model_param.model_name

        if model_name == consts.HETERO_FAST_SBT_LAYERED:
            return feature_importance_with_anonymous_converter(model_meta, model_param)
        elif model_name == consts.HETERO_FAST_SBT_MIX:
            return feature_importance_with_anonymous_converter(model_meta, model_param)
        else:
            raise ValueError('model name {} is illegal'.format(model_name))
