import yt.wrapper as yt_wrapper
from datacloud.dev_utils.logging.logger import get_basic_logger
from datacloud.dev_utils.yt import yt_utils
from datacloud.dev_utils.time.utils import assert_date_str
from datacloud.dev_utils.time.utils import now_str
from datacloud.dev_utils.status_db.task import Task, Status
from datacloud.model_applyer.utils import model_runner
from datacloud.model_applyer.tables.models_config_table import ApiModelsConfigTable


logger = get_basic_logger(__name__)


COMMAND_SEPRARATOR = '#'
TMP_FOLDER = '//tmp'


def build_key(model_config, date_str):
    return COMMAND_SEPRARATOR.join(
        [model_config.partner_id, model_config.score_name, date_str])


def feature_collection_to_str(required_features):
    return '-'.join(['xprod-features'] + [str(feature) for feature in required_features])


def get_active_models():
    config = ApiModelsConfigTable()
    for model_class, model_config in config.get_all_active_model_classes_and_configs():
        yield model_class, model_config


def _detect_ready_apply_model(model_config):
    yt_client = yt_utils.get_yt_client()
    logger.info('Detect ready apply_api_model: {} {}'.format(model_config.partner_id, model_config.score_name))
    yt_utils.create_folders(model_config.score_dir, yt_client)
    last_score_table = yt_utils.get_last_table(model_config.score_dir)
    last_score_date = last_score_table.split('/')[-1]

    possible_dates = []
    for feature in model_config.required_features:
        dates = feature.get_ready_dates(yt_client)
        dates = [date for date in dates if date > last_score_date]
        possible_dates.append(set(dates))

    if len(possible_dates) == 0:
        return

    overlap = possible_dates[0]
    for it in possible_dates[1:]:
        overlap &= it

    if not overlap:
        return

    date_str = max(overlap)
    yield build_key(model_config, date_str), {
        'date_str': date_str,
        'partner_id': model_config.partner_id,
        'score_name': model_config.score_name
    }


def run_apply_model(task):
    yt_client = yt_utils.get_yt_client()
    partner_id = task.data['partner_id']
    score_name = task.data['score_name']
    date_str = task.data['date_str']
    assert_date_str(date_str)
    model_class, model_config = ApiModelsConfigTable().get_model_class_and_config(
        partner_id, score_name)

    combined_features_table = yt_wrapper.ypath_join(
        TMP_FOLDER, feature_collection_to_str(model_config.required_features) + ' ' + date_str)
    with yt_client.Transaction():
        is_get_features = not yt_utils.check_table_exists(
            combined_features_table, yt_client)
        result_score_table = model_runner.run_model(
            model_class, model_config,
            yt_client=yt_utils.get_yt_client(), date_str=date_str,
            is_get_features=is_get_features,
            is_apply_model=True,
            combined_features_table_path=combined_features_table)
        logger.info('Set link {} to: {}'.format(model_config.current_score_table,
                                                result_score_table))
        yt_client.link(result_score_table, model_config.current_score_table,
                       force=True)

        current_time = now_str()
        key = COMMAND_SEPRARATOR.join([partner_id, score_name, date_str])
        return [
            task.make_done(),
            # TODO: Find step afte blend scores and set it
            Task(
                'check_models_stability', key, Status.READY,
                {
                    'partner_id': partner_id,
                    'score_name': score_name,
                    'date_str': date_str,
                },
                current_time, current_time
            )
        ]


def detect_ready(date_time):
    for _, model_config in get_active_models():
        for key, data in _detect_ready_apply_model(model_config):
            yield key, data
