from django.conf import settings
import requests
import json
import sys
import warnings
from retrying import retry
from .util import escape_string
from .exceptions import ClickhouseException

if not sys.warnoptions:
    warnings.simplefilter("ignore")


class ClickhouseClient(object):

    def __init__(self,
                 connection_timeout=1500,
                 readonly=False
                 ):

        self.connection_timeout = connection_timeout
        self.url = 'http{}://{}:{}/?database={}'.format(
            's'*settings.CLICKHOUSE_SSL,
            settings.CLICKHOUSE_HOST,
            settings.CLICKHOUSE_PORT,
            settings.CLICKHOUSE_NAME
        )
        self.readonly = readonly
        self.session = requests.Session()
        self.session.verify = False
        self.session.headers.update(
            {'X-ClickHouse-User': settings.CLICKHOUSE_USER, 'X-ClickHouse-Key': settings.CLICKHOUSE_PASS}
        )

    def execute(self, query, query_params=None):
        if self.readonly and not query.lstrip()[:10].lower().startswith('select'):
            raise ClickhouseException('Clickhouse client is in readonly state')
        query = self._prepare_query(query, query_params=query_params)
        r = self._post(query)
        if r.status_code != 200:
            raise ClickhouseException('{}\n{}\n=====\n{}\n===='.format(r.status_code, r.content, query))
        return r.content.decode('utf8')

    def insert(self, query, query_params=None, escape_str=True):
        if self.readonly:
            raise ClickhouseException('Clickhouse client is in readonly state')
        query = self._prepare_query(query, query_params=query_params, escape_str=escape_str)
        assert query.lstrip()[:10].lower().startswith('insert')
        r = self._post(query)
        if r.status_code != 200:
            raise ClickhouseException('{}\n{}'.format(r.status_code, r.content[:200]))

    def select(self, query, query_params=None, deserialize=True):
        prepared_query = self._prepare_query(query, query_params=query_params)
        prepared_query_fmt_json = prepared_query.rstrip().rstrip(';') + ' FORMAT JSONCompact'
        if not self.readonly and '&max_threads=3' not in self.url:
            self.url += '&max_threads=3'
        assert prepared_query_fmt_json.lstrip()[:10].lower().startswith(('select', 'with'))
        r = self._post(prepared_query_fmt_json)
        if r.status_code != 200:
            raise ClickhouseException('{}\n{}\n=====\n{}\n===='.format(r.status_code, r.content, prepared_query_fmt_json))
        if deserialize:
            return json.loads(r.content.decode('utf-8'))['data']
        else:
            return str(r.content)

    def select_tsv(self, query, query_params=None):
        query = self._prepare_query(query, query_params=query_params)
        query = query.rstrip().rstrip(';')
        if not self.readonly and not self.url.endswith('&max_threads=3'):
            self.url += '&max_threads=3'
        assert query.lstrip()[:10].lower().startswith('select')
        r = self._post(query)
        if r.status_code != 200:
            raise ClickhouseException('{}\n{}\n=====\n{}\n===='.format(r.status_code, r.content, query))
        return r.content

    def select_csv(self, query, query_params=None, with_names=False):
        query = self._prepare_query(query, query_params=query_params)
        query = query.rstrip().rstrip(';') + ' FORMAT ' + ('CSVWithNames' if with_names else 'CSV')
        if not self.readonly and not self.url.endswith('&max_threads=3'):
            self.url += '&max_threads=3'
        # assert query.lstrip()[:10].lower().startswith('select')
        r = self._post(query)
        if r.status_code != 200:
            raise ClickhouseException('{}\n{}\n=====\n{}\n===='.format(r.status_code, r.content, query))
        return r.content

    @staticmethod
    def _prepare_query(query, query_params=None, escape_str=True):
        escape = escape_string if escape_str else lambda x: x
        if query_params is not None:
            query = query.format(**{
                key: str(item) if key in (
                     'metric_ids',
                     'job_ids',
                     'cases_with_tag',
                     'tags',
                     'targets',
                     'query_addon',
                     'query_time',
                ) else escape(str(item))
                for key, item in query_params.items()
            })
        return query

    @retry(stop_max_delay=30000,
           wait_fixed=3000,
           stop_max_attempt_number=10)
    def _post(self, query):
        return self.session.post(self.url, data=query, timeout=10)


__all__ = ['ClickhouseClient']
