# -*- coding: utf-8 -*-
import logging
import traceback as tb
from os.path import dirname, join
import pandas as pd
from yt import wrapper as yt
from yql.api.v1.client import YqlClient
from yql.client.operation import YqlOperationShareIdRequest

from robot import TOKEN, YQL_QUERY


logger = logging.getLogger(__name__)


class YQLWorker(object):
    def __init__(self, issue=None, query_params=None):
        super(YQLWorker, self).__init__()
        id_, cat, region = query_params['id'], query_params['category'], query_params['region']
        logger.info(u'{}: init YQLWorker for {} in {}'.format(issue.key, cat, region))

        self.issue = issue
        self.id, self.cat, self.region = id_, cat, region

        query_params['region'] = u', '.join([u'"{}"'.format(reg.strip()) for reg in region.split(u',')])
        self.query_params = query_params
        self.yql_query = YQL_QUERY.format(**self.query_params)

        self.request, self.result, self.results = None, None, None
        self.res_dfs = []

        self.few_competitors, self.less_competitors = [], False
        logger.info(u'{}: YQLWorker for {} in {} initialized'.format(issue.key, cat, region))

    def run(self):
        logger.info(u'{}: run YQLWorker for {} in {}'.format(self.issue.key, self.cat, self.region))

        yql_client = YqlClient(token=TOKEN)
        request = yql_client.query(self.yql_query, syntax_version=1)
        request.run()
        self.request = request
        self.result = self.request.get_results()

        if not self.result.is_success:
            raise YQLError(u'{}: result for {} in {} isn\'t success!'.format(self.issue.key, self.cat, self.region))

        self._tables_to_dfs()
        logger.info(u'{}: request for {} in {} completed'.format(self.issue.key, self.cat, self.region))

        self._create_result_wb()
        self._check_competitors()

    def _tables_to_dfs(self):
        yt_client = yt.YtClient(token=TOKEN, proxy='hahn')

        for tab_name in ['%i_full_data_months', '%i_full_data_period', '%i_benchmark_month', '%i_benchmark_period']:
            table_name = tab_name % self.id
            logger.debug(u'{}: prepare {} for {} in {}'.format(self.issue.key, table_name, self.cat, self.region))

            table = yt_client.read_table(
                '//tmp/robot-btz/{issue}/{table_name}'.format(issue=self.issue.key, table_name=table_name),
                format='<encode_utf8=%false>json')
            df = pd.DataFrame(list(table))
            if df.empty:
                raise EmptyResultError(u'{}: {} for {} in {} is empty'.format(self.issue.key, table_name, self.cat,
                                                                              self.region))

            self.res_dfs.append(df)

    def _create_result_wb(self):
        logger.info(u'{}: create result workbook for {} in {}'.format(self.issue.key, self.cat, self.region))

        xlsx_path = join(dirname(dirname(__file__)), u'{}_{}.xlsx'.format(self.cat, self.region))
        writer = pd.ExcelWriter(xlsx_path)
        for df_num, df in enumerate(self.res_dfs, 1):
            df.to_excel(writer, u'Result #%s' % df_num, index=False, encoding='utf-8')
        writer.save()

        self.xlsx_path = xlsx_path

    def _check_competitors(self):
        logger.info(u'{}: check competitors for {} in {}'.format(self.issue.key, self.cat, self.region))
        few_competitors = []
        device_names = {'SUM_': u'все устройства',
                        'PC': u'десктоп',
                        'Mob': u'мобильные устройства'}

        def check_row(row, prefix__):
            if row['ClientType'] == u'Клиент' or row['QueryBrandType'] == u'Суммарно':
                pass

            else:
                if int(row['%sCompCount' % prefix__]) < 3:
                    warning = u'* {} запросы, {}'.format(row['QueryBrandType'].lower(), device_names[prefix__])
                    if warning not in few_competitors:
                        logger.debug(
                            u'{}: few {} for {} in {} ({})'.format(self.issue.key,
                                                                   '%sCompCount' % prefix__,
                                                                   self.cat,
                                                                   self.region,
                                                                   row['QueryBrandType'].lower())
                        )
                        few_competitors.append(warning)

                    for col in ['Shows', 'Clicks', 'Cost']:
                        row['%s%s' % (prefix__, col)] = 0

                elif not self.less_competitors and int(row['%sCompCount' % prefix__]) < self.query_params['comp_num']:
                    self.less_competitors = True

            return row

        df = self.res_dfs[-1]
        for prefix in ['SUM_', 'PC', 'Mob']:
            df = df.apply(lambda row: check_row(row, prefix), axis=1)
        self.res_dfs[-1] = df

        if few_competitors:
            few_competitors.insert(0, u'{}, {}'.format(self.cat, self.region))
            few_competitors += [u'\n']

            logger.warning(u'{}: not enough competitors for {} in {}'.format(self.issue.key, self.cat, self.region))

        else:
            logger.info(u'{}: enough competitors for {} in {}'.format(self.issue.key, self.cat, self.region))

        self.few_competitors = few_competitors

        if self.less_competitors:
            logger.warning(
                u'{}: less than {} competitors for {} in {}'.format(self.issue.key,
                                                                    self.query_params['comp_num'],
                                                                    self.cat,
                                                                    self.region)
            )

    def get_public_url(self):
        share_req = YqlOperationShareIdRequest(self.result.operation_id)
        share_req.run()
        return u'https://yql.yandex-team.ru/Operations/{}'.format(share_req.json)


class YQLError(Exception):
    def __init__(self, msg=None):
        self.msg = msg
    pass


class EmptyResultError(Exception):
    def __init__(self, msg=None):
        self.msg = msg
    pass


def yql_worker(params):
    issue, query_params = params['issue'], params['query_params']

    worker = YQLWorker(issue, query_params)
    result = {'worker': worker, 'status': 'OK'}

    try:
        worker.run()
    except YQLError as yql_err:
        logger.error(yql_err.msg)
        issue.comments.create(text=u'Запрос завершился ошибкой. ((%s Ссылка на расчет в YQL)).\n'
                                   u'<{Подробнее:'
                                   u'\n%%%%\n%s\n%%%%'
                                   u'}>' % (worker.get_public_url(),
                                            u'\n'.join([err.format_issue() for err in worker.result.errors])
                                            ),
                              summonees=['vbatraev', 'k-n-gorelov'])
        result['status'] = 'FAIL'

    except EmptyResultError as er_err:
        logger.error(er_err.msg)
        issue.comments.create(text=u'Пустой результат расчета. ((%s Ссылка на расчет в YQL)).\n'
                                   % worker.get_public_url(),
                              summonees=['vbatraev', 'k-n-gorelov'])
        result['status'] = 'FAIL'

    except Exception:
        logger.exception('%s: Oops!', issue.key)
        issue.comments.create(text='Что-то сломалось...<{Подробнее:'
                                   '\n%%%%\n%s\n%%%%'
                                   '}>' % tb.format_exc(),
                              summonees=['vbatraev', 'k-n-gorelov']
                              )
        result['status'] = 'FAIL'

    return result
