from yt.wrapper import ypath_join, create_table_switch
import yt.wrapper as yt_wrapper
from datacloud.launcher.lib.services_logs.helpers import YtLogFilter
from datacloud.launcher.lib.services_logs.qloud import RawLogFilter, QLOUD_LOG_SCHEMA
from datacloud.dev_utils.logging.logger import get_basic_logger

logger = get_basic_logger(__name__)


SERVICE_EVENTS_SCHEMA = [
    {'name': 'qloud_project', 'type': 'string'},
    {'name': 'qloud_application', 'type': 'string'},
    {'name': 'qloud_environment', 'type': 'string'},
    {'name': 'qloud_component', 'type': 'string'},
    {'name': 'qloud_instance', 'type': 'string'},
    {'name': 'timestamp', 'type': 'string'},
    {'name': 'event_tag', 'type': 'string'},
    {'name': 'context', 'type': 'any'},
    {'name': 'message', 'type': 'string'},
    {'name': 'partner_id', 'type': 'string'},
    {'name': 'external_id', 'type': 'string'},
    {'name': 'scenario_start_id', 'type': 'string'},
    {'name': 'communication_id', 'type': 'string'},
    {'name': 'id_type', 'type': 'string'},
    {'name': 'id_value', 'type': 'string'},
    {'name': 'host', 'type': 'string'},
]


SERVICES = {
    ('x-products', 'datacloud-score-api', 'ydb-production'): ('datacloud_score_api', 'production'),
    ('x-products', 'datacloud-score-api', 'production'): ('datacloud_score_api', 'production'),
    ('x-products', 'datacloud-score-api', 'production-sandbox'): ('datacloud_score_api', 'production_sandbox'),
}


class AllServicesLogFilter(YtLogFilter):
    """ Manipulates raw qloud log from x-products services """

    def __init__(self, root, **kwargs):
        super(AllServicesLogFilter, self).__init__()
        self._qloud_filter = RawLogFilter(root, **kwargs)
        uniq_services, envs_index = _build_mapper_index(SERVICES)
        self._uniq_services = uniq_services
        self._envs_index = envs_index
        self._tables = AllServicesLogTables(root, services=uniq_services, **kwargs)

    def grep_stream_tables(self, table_pathes):
        """ Filters table like "//home/logfeller/logs/qloud-runtime-log/stream/5min/2017-10-18T15:20:00" """
        mapper = _build_log_mapper(self._envs_index)
        return super(AllServicesLogFilter, self).grep_stream_tables(mapper, table_pathes)

    def list_unfiltered_stream(self):
        qloud_tables = []
        for history_root, filtered in self._qloud_filter.list_filtered_stream():
            qloud_tables.extend(filtered)
        return qloud_tables

    def list_filtered_stream(self):
        for service, env_type in self._uniq_services:
            service_tables = self._tables.get_service(service, env_type)
            yield service_tables.events_history_path, self.yt_list(service_tables.events_stream_path)
            yield service_tables.full_history_path, self.yt_list(service_tables.full_stream_path)

    def list_filtered_events_stream(self):
        tables = []
        for service, env_type in self._uniq_services:
            service_tables = self._tables.get_service(service, env_type)
            tables.extend(self.yt_list(service_tables.events_stream_path))
        return tables

    def get_events_last_ndays_path(self, service, env_type):
        service_tables = self._tables.get_service(service, env_type)
        return service_tables.events_last_ndays_path

    def get_events_history_root(self, service, env_type):
        service_tables = self._tables.get_service(service, env_type)
        return service_tables.events_history_path

    def get_yt_client(self):
        return self._tables.yt_client

    def get_stream_output_tables(self, time_str):
        for service, env_type in self._uniq_services:
            service_tables = self._tables.get_service(service, env_type)

            # keep order, see table indexes in the mapper
            yield service_tables.get_full_stream_table(time_str)
            yield service_tables.get_event_stream_table(time_str)


def _build_mapper_index(env_to_service):
    service_to_envs = {}
    for env in sorted(env_to_service):
        service = env_to_service[env]
        service_envs = service_to_envs.setdefault(service, [])
        service_envs.append(env)
    uniq_services = sorted(service_to_envs)
    uniq_services_index = {s: i for i, s in enumerate(uniq_services)}
    envs_index = {e: uniq_services_index[s] for e, s in env_to_service.iteritems()}
    return uniq_services, envs_index


def _build_log_mapper(envs_index):
    def mapper(record):
        qloud_project = record['qloud_project']
        qloud_application = record['qloud_application']
        qloud_environment = record['qloud_environment']
        env_key = (qloud_project, qloud_application, qloud_environment)
        if env_key not in envs_index:
            raise StopIteration()

        table_index = envs_index[env_key]
        total_table_count = 2
        full_log_index = table_index * total_table_count + 0
        event_log_index = table_index * total_table_count + 1

        yield create_table_switch(full_log_index)
        yield record

        event = _get_event_record(record)
        if event:
            yield create_table_switch(event_log_index)
            yield event
    return mapper


def _get_event_record(record):
    fields = record['fields']
    if not fields:
        return None
    context = fields.get('context')
    if not context:
        return None
    event_tag = context.get('tag')
    if not event_tag:
        return None
    keep_fields = (
        'qloud_project',
        'qloud_application',
        'qloud_environment',
        'qloud_component',
        'qloud_instance',
        'timestamp',
        'message',
        'host',
    )
    result = {
        'event_tag': event_tag,
        'partner_id': context.get('partner_id'),
        'external_id': context.get('external_id'),
        'id_type': context.get('id_type'),
        'id_value': context.get('id_value'),
        'scenario_start_id': context.get('scenario_start_id'),
        'communication_id': context.get('communication_id'),
        'context': context,
    }
    for f in keep_fields:
        result[f] = record.get(f)
    return result


class AllServicesLogTables:

    def __init__(self, root, services):
        self._root = root
        self._service_tables = {}
        for service, env_type in services:
            tables = ServiceLogTables(
                root,
                env_type=env_type,
                service_name=service,
                # yt_cluster=self.yt_cluster, yt_client=self.yt_client
            )
            self._service_tables[(service, env_type)] = tables

    def get_service(self, name, env_type):
        return self._service_tables[(name, env_type)]


class ServiceLogTables:

    def __init__(self, root, env_type, service_name, **kwargs):
        self._root = root
        logs_path = str(ypath_join(root, 'services/logs', service_name, env_type))

        self.events_path = str(ypath_join(logs_path, 'events'))
        self.full_path = str(ypath_join(logs_path, 'full'))

        self.events_stream_path = self._get_stream(self.events_path)
        self.events_last_ndays_path = self._get_events_last_ndays(self.events_path)
        self.full_stream_path = self._get_stream(self.full_path)
        self.events_history_path = self._get_history(self.events_path)
        self.full_history_path = self._get_history(self.full_path)

    @staticmethod
    def _get_stream(path):
        return str(ypath_join(path, 'stream/5min'))

    @staticmethod
    def _get_events_last_ndays(path):
        return str(ypath_join(path, 'last_ndays'))

    @staticmethod
    def _get_history(path):
        return str(ypath_join(path, 'history'))

    def _get_table(self, path, schema, table_name):
        return yt_wrapper.TablePath(
            ypath_join(path, table_name),
            attributes={
                'schema': schema
            }
        )

    def get_event_stream_table(self, table_name):
        return self._get_table(
            self._get_stream(self.events_path),
            schema=SERVICE_EVENTS_SCHEMA,
            table_name=table_name,
        )

    def get_full_stream_table(self, table_name):
        return self._get_table(
            self._get_stream(self.full_path),
            schema=QLOUD_LOG_SCHEMA,
            table_name=table_name,
        )
