# -*- coding: utf-8 -*-
from datacloud.dev_utils.solomon import solomon_utils
from datacloud.stability import stability
from datacloud.dev_utils.solomon.solomon_utils import is_date, str2ts
from datacloud.stability.scores_psi_table import (
    ScoresPsiTable, DEFAULT_PATH as PSI_SCORE_TABLE_PATH
)
from datacloud.dev_utils.logging.logger import get_basic_logger

logger = get_basic_logger(__name__)


__all__ = [
    'run_calculate_score_psi_table',
    'calculate_score_psi_table'
]


# ------------ SOLOMON FUNCTIONS START ------------
def make_sensor_psi(score_name, timestamp, value):
    '''
    Makes sensor for psi of the score table
    '''
    return {
        'labels': {
            'score_name': score_name,
            'type': 'PSI'
        },
        'ts': timestamp,
        'value': value
    }


def make_sensor_hist(score_name, timestamp, bin_number, value):
    '''
    Makes sensor for one bin of histrogram
    '''
    return {
        'labels': {
            'score_name': score_name,
            'type': 'histogram',
            'bin': bin_number
        },
        'ts': timestamp,
        'value': value
    }


def load_row_to_solomon(row):
    '''
    Upload row with fields ['date', 'score_name', 'PSI', 'norm_hist']
    to solomon
    row['date'] should have 'YYYY-MM-DD' format
    '''
    score_name = row['score_name']
    date = row['date']
    if date != 'current' and is_date(date):
        timestamp = str2ts(date)
        sensors = [make_sensor_psi(score_name, timestamp, row['PSI'])]
        for i in range(len(row['norm_hist'])):
            sensors.append(make_sensor_hist(score_name, timestamp, i, row['norm_hist'][i]))
        solomon_utils.post_sensors_to_solomon('datacloud', 'score', 'stability', sensors)
        logger.info('Loaded score {} calcuated on {}'.format(score_name, date))


def load_whole_psi_table():
    for row in ScoresPsiTable().list_records():
        load_row_to_solomon(row)
# ------------ SOLOMON FUNCTIONS END ------------


def run_calculate_score_psi_table(yt_client, input_table, score_name, date, score_type):
    if not yt_client.exists(PSI_SCORE_TABLE_PATH):
        calculate_score_psi_table(yt_client, input_table, score_name, date, score_type,
                                  is_reference=True)
        return

    reference_rows = list(ScoresPsiTable().get_reference_rows(score_name, score_type))
    assert len(reference_rows) < 2, 'Check that only one reference for score_name'

    if len(reference_rows) == 1:
        calculate_score_psi_table(yt_client, input_table, score_name, date, score_type,
                                  is_reference=False)
    else:
        calculate_score_psi_table(yt_client, input_table, score_name, date, score_type,
                                  is_reference=True)


def calculate_score_psi_table(yt_client, input_table, score_name, date, score_type,
                              is_reference=False):
    '''
    Function takes path to input_table with scores and make histogram with
    100 bins for it. Then PSI is calculated if there is a reference historgram
    for input score_name in a special table marked as 'reference'. It also could
    upload hist as reference table by means of 'is_refence flag'.

    Same row is sent to solomon for monitoring
    '''
    table_size = yt_client.row_count(input_table)
    score_psi_table = ScoresPsiTable()

    tmp_table = '//tmp/xprod_stability_target'
    stability.FeaturesToStabilityBinsConverter(yt_client, n_bins=100, table_type='scores')(input_table, tmp_table)
    logger.info('Processed table: {}'.format(input_table))
    target_hist = next(yt_client.read_table(tmp_table))['norm_hist']
    if is_reference:
        reference_table = input_table
        psi = 0
    else:
        is_found = False
        if yt_client.exists(PSI_SCORE_TABLE_PATH):
            reference_row = next(score_psi_table.get_reference_rows(score_name, score_type))
            reference_table = reference_row['reference_table']
            psi = stability.calculate_psi(target_hist, reference_row['norm_hist'])
            is_found = True

        if not is_found:
            psi = None
            reference_table = None
            logger.warning('Reference histogram for input score name was not found!')

    row_out = {
        'score_name': score_name,
        'date': date,
        'score_type': score_type,
        'PSI': psi,
        'reference_table': reference_table,
        'norm_hist': target_hist,
        'input_table': input_table,
        'is_reference': is_reference,
        'table_size': table_size
    }

    score_psi_table.add_record(**row_out)
    logger.info('PSI uploaded to YT for table: {}'.format(input_table))

    load_row_to_solomon(row_out)
    logger.info('PSI uploaded to Solomon for table: {}'.format(input_table))
