from clickhouse_driver import Client
from load.projects.lunaparkapi.settings import base


class Aggregator:

    def __init__(self, job_obj=None, tag=''):
        self.job_obj = job_obj
        self.tag = tag

        self.client = Client(
            host=base['CLICKHOUSE_HOST'],
            port=base['CLICKHOUSE_PORT'],
            database=base['CLICKHOUSE_DATABASE'],
            user=base['CLICKHOUSE_USER'],
            password=base['CLICKHOUSE_PASSWORD'],
            secure=True,
            verify=False,
            settings={'joined_subquery_requires_alias': 0}
        )

    @staticmethod
    def count_percentage(data, total_amount):
        """
        :param data: raw data taken from clickhouse
        :type data: list
        :param total_amount: total amount of responses in test
        :return percentage for each item
        :rtype list
        """
        return [round(float(value) * 100 / float(total_amount), 3) for value in data]


class ProtoCodesAggregator(Aggregator):
    """HTTP codes distribution"""
    def aggregate(self):
        result = {'codes': set(), 'cases': {}}
        data = self.get_raw_data()
        percentage = self.count_percentage(
            data=[count[2] for count in data],
            total_amount=sum([item[2] for item in data if item[1] == ''])
        )
        for counter, value in enumerate(data):
            case = 'overall' if value[1] == '' else value[1]
            result['cases'].setdefault(case, {})
            result['cases'][case][str(value[0])] = {
                'amount': value[2],
                'percentage': percentage[counter]
            }
            result['codes'].add(str(value[0]))
        result['codes'] = list(result['codes'])
        return result

    def get_raw_data(self):
        """
        Get data from clickhouse
        :return proto code and amount of entries with this code
        :rtype Union[Union[str, float]]
        """
        sql = '''select {param}, tag, toFloat32(sum(cnt))
            from {sql_table}
            where job_id={job}
            and job_date=toDate({job_date})
             group by {param}, tag
            order by {param}'''

        raw_data = self.client.execute(
            sql.format(param=self.param, sql_table=self.sql_table,
                       job=self.job_obj.id, job_date=self.job_obj.date)
        )
        return raw_data

    @property
    def sql_table(self):
        return 'loaddb.proto_codes_buffer'

    @property
    def param(self):
        return 'code'


class NetCodesAggregator(ProtoCodesAggregator):
    """Net codes distribution"""

    @property
    def sql_table(self):
        return 'loaddb.net_codes_buffer'


class CumulativeQuantilesAggregator(Aggregator):
    """Cumulative quantiles by case"""

    @property
    def quantiles(self):
        return ['50%', '75%', '80%', '85%', '90%', '95%', '98%', '99%']

    @property
    def sql_table(self):
        return 'loaddb.rt_microsecond_histograms_buffer'

    def get_raw_data(self):
        sql = '''
        select tag, quantilesExactWeighted(0.5, 0.75, 0.8, 0.85, 0.9, 0.95, 0.98, 0.99)(bin, cnt)
        from {table}
            where job_id={job}
            and job_date=toDate({job_date})
            and time >= toDateTime({start})
            and time <= toDateTime({end})
            group by tag
            order by tag
        '''
        raw_data = self.client.execute(
            sql.format(
                table=self.sql_table,
                job=self.job_obj.id, job_date=self.job_obj.date,
                start=self.job_obj.start, end=self.job_obj.end
            )
        )
        return raw_data

    def aggregate(self):
        result = {'quantiles': self.quantiles, 'cases': {}}
        for case, value in self.get_raw_data():
            if case == '':
                case = 'overall'
            result['cases'][case] = [float(i)/1000 for i in value]
        return result
