# -*- coding: utf-8 -*-
from hashlib import sha1 as hash_func
import logging
from os import environ, mkdir
from os.path import dirname, exists, join
from random import choice
from shutil import rmtree
from startrek_client import Startrek
from time import sleep
import traceback as tb
import yaml

from const import CONFIG, PATTERN, SECRET, USER_VARS
from report_worker import calculate_mini_sources
from robot.errs import CurrencyError, NoReportsError, WorkerError
from sources import make_login_sources
from utils import create_s3_connection, get_dates, normalize_login

logger = logging.getLogger(__name__)


def task_worker(task):
    try:
        issue = task.get_issue()
        if issue.assignee.login == SECRET['PASSPORT']['login'] or task.debug:
            task.calculate()
            task.paste_results()
            task.stop_progress()
        else:
            logger.debug('%s: assignee has been changed', task.issue.key)

    except WorkerError:
        pass

    except CurrencyError as exc:
        logger.error('%s: %s', task.issue.key, exc.message)
        task.stop_progress('need_info', comment={'text': u'В отчетах логина разная валюта:\n{}'.format(exc.message),
                                                 'summonees': ['vbatraev']})
    except:
        logger.exception('%s: Oops!', task.issue.key)
        task.stop_progress('need_info',
                           comment={'text': u'Что-то сломалось...<{Подробнее:\n%%%%\n%s\n%%%%\n}>' % tb.format_exc(),
                                    'summonees': ['vbatraev']})
    finally:
        if not task.debug:
            task.rmdir_()
        return task


class TATask(object):
    def __init__(self, issue=None, debug=False):
        self.issue, self.debug = issue, debug
        self._get_reports()
        self.path = join(dirname(dirname(__file__)), self.issue.key)

        self.mini_src, self.ta_src = {}, {}
        self.comments = []

        if issue.status.key == 'inProgress':
            self.was_in_progress = True
        else:
            self.was_in_progress = False
            self.mkdir_()

    def calculate(self):
        if not self.debug and not self.was_in_progress:
            self.issue.comments.create(text=u'Задача в работе.')
        self.get_mini_sources()
        self.calculate_mini_sources()
        self.make_ta_sourses()

    def is_hard(self):
        if self.params['days'] == 30:
            return False
        else:
            return any([report_name == 'search_queries' for report_name in self.reports.keys()])

    def _get_reports(self):
        description = PATTERN.match(self.issue.description).groupdict()
        cs_params = yaml.safe_load(description.get('cs_params', {}))
        ta_params = yaml.safe_load(description.get('ta_params', {}))

        cids = [str(cid.strip()) for cid in description.get('cids', '').split('\n') if cid.strip()]
        self.params = {
            'days': cs_params.get('days', 30),
            'first_date': cs_params.get('from_date', None),
            'last_date': cs_params.get('to_date', None),
            'logins': [normalize_login(login) for login in description.get('logins', '').split('\n') if login.strip()],
            'cids': [int(cid) for cid in cids],
            'hashed_cids': hash_func(','.join(sorted(cids))).hexdigest()
        }
        self.split_logins = True if ta_params.get('split_logins', u'Нет') in (u'Да', u'Yes') else False

        self.reports = {}
        for report_name, report in CONFIG['REPORTS'].iteritems():
            if ta_params.get(report['option'], 0) == 1:
                self.reports[report_name] = self._get_report_params(report_name, self.params['days'],
                                                                    self.params['first_date'], self.params['last_date'])

        if not self.reports:
            if sum(cs_params.get(option, 0) for option in ['make_keywords_report', 'make_mobile_report']) == 0:
                self.stop_progress('need_info', comment={'text': u'Не выбран ни один отчет!',
                                                         'summonees': [self.issue.createdBy.login, 'vbatraev']})
                if not self.debug:
                    issue = self.get_issue()
                    issue.update(assignee='robot-constructor-cs')

                self.rmdir_()
                raise NoReportsError('no reports selected')

    def _get_report_params(self, report_name, days, first_date=None, last_date=None):
        if report_name == 'search_queries' and days == 30 and first_date is None and last_date is None:
            arc_name, rev_name = '%s_agg' % report_name, CONFIG['REPORTS'][report_name]['rev_agg']
            dates = get_dates(days, from_yt=True, report_name=report_name)
        else:
            arc_name, rev_name = report_name, CONFIG['REPORTS'][report_name]['rev']
            dates = get_dates(days, first_date=first_date, last_date=last_date)

        return {
            'arc_name': arc_name,
            'rev': environ.get(rev_name, USER_VARS[rev_name]),
            'syntax_version': USER_VARS.get('%s_SYN' % rev_name, 0),
            'first_date': dates['first_date'],
            'last_date': dates['last_date']
        }

    def paste_results(self):
        logger.debug('%s: paste results', self.issue.key)
        if not self.debug:
            for login, obj in self.ta_src.iteritems():
                self.issue.comments.create(text=u'Highlight result:\n(({} {}))'.format(
                    CONFIG['NAMES']['url'].format(bucket=CONFIG['S3_API']['ta_bucket'], host=CONFIG['S3_API']['host'],
                                                  key=obj.key),
                    obj.key))

    def get_mini_sources(self):
        logger.info('%s: get mini-sources', self.issue.key)
        conn = create_s3_connection()
        bucket = conn.get_bucket(CONFIG['S3_API']['mini_bucket'])

        for report_name in self.reports:
            self.mini_src[report_name] = {}
            for login in self.params['logins']:
                obj = bucket.get_key(CONFIG['NAMES']['mini_src'].format(
                    report_name=report_name, hashed_cids=self.params['hashed_cids'], login=login,
                    date_1=self.reports[report_name]['first_date'], date_2=self.reports[report_name]['last_date']))
                if obj:
                    if self.reports[report_name]['rev'] == obj.get_metadata('rev'):
                        logger.debug('%s: %s found', self.issue.key, obj.key)
                        self.mini_src[report_name][login] = obj

            if not self.mini_src[report_name]:
                logger.debug('%s: mini-sources for %s-report not found', self.issue.key, report_name)

        conn.close()

    def calculate_mini_sources(self):
        # Запуск YQL-запросов и заполнение self.mini_src
        calculate_mini_sources(self)

        if not self.split_logins and self.reports:
            self._validate_mini_sources()

    def _validate_mini_sources(self):
        logger.info('%s: validate mini-sources', self.issue.key)
        one_clid_logins = []

        rand_report_name = choice(self.reports.keys())
        clids = {}
        for login, obj in self.mini_src[rand_report_name].iteritems():
            clid = obj.get_metadata('clid')
            if clid != 'Empty':
                if clids.get(clid) is None:
                    clids[clid] = [login]
                else:
                    clids[clid].append(login)

        for clid, logins in clids.iteritems():
            if len(logins) > 1:
                logger.warning('{}: ClientID {} - {} logins'.format(self.issue.key, clid, len(logins)))
                one_clid_logins.append(u'* {} - {}'.format(clid, u', '.join(logins)))

        if one_clid_logins:
            warning = u'Некоторые логины из задания относятся к одному ClientID, ' \
                      u'что приведет к замножению данных в отчетах Техноаудита:'
            self.comments.append(u'\n'.join([warning] + one_clid_logins))

    def make_ta_sourses(self):
        # Создание tar.gz на каждый логин с заливкой на MDS
        logger.info('%s: make ta-sources', self.issue.key)

        conn = create_s3_connection()
        bucket = conn.get_bucket(CONFIG['S3_API']['ta_bucket'])
        for login in self.params['logins']:
            self.ta_src[login] = make_login_sources(self, login, bucket)
            logger.info('%s: %s created', self.issue.key, self.ta_src[login].key)

    def mkdir_(self):
        self.rmdir_()
        mkdir(self.path)

    def rmdir_(self):
        if exists(self.path):
            rmtree(self.path)

    def get_issue(self):
        st_client = Startrek(useragent='', token=SECRET['token'])
        issue = st_client.issues[self.issue.key]
        return issue

    def stop_progress(self, transition='stop_progress', comment=None):
        logger.info('%s: stop_progress (transition %s)', self.issue.key, transition)
        if not self.debug:
            if comment:
                self.issue.comments.create(text=comment.get('text', ''),
                                           summonees=comment.get('summonees', []))

            if transition == 'stop_progress':
                if self.comments:
                    self.issue.comments.create(text=u'\n'.join(self.comments), summonees=[self.issue.createdBy.login])

                for i in range(4):
                    try:
                        issue = self.get_issue()
                        issue.update(assignee='robot-ta-report')
                        break
                    except Exception as exc:
                        if getattr(exc, 'reason') == 'Conflict':
                            sleep(10)
                        else:
                            raise exc

            self.issue.transitions[transition].execute()
