# -*- coding: utf-8 -*-
import json

from datetime import datetime, date
from django.conf import settings
from io import StringIO
from requests.exceptions import ConnectionError, Timeout

from events.common_app.utils import requests_session


class ClickHouseError(Exception):
    pass


class ClickHouseQueryError(ClickHouseError):
    pass


class ClickHouseConnectionError(ConnectionError, ClickHouseError):
    pass


class ClickHouseTimeoutError(Timeout, ClickHouseError):
    pass


class ClickHouseUnknownError(ClickHouseError):
    pass


class ClickHouseClient:
    def __init__(self, host, port, db, user, password):
        self.url = 'https://{host}:{port}/?database={db}'.format(host=host, port=port, db=db)
        self.auth = {
            'X-ClickHouse-User': user,
            'X-ClickHouse-Key': password,
        }

    def get_summary(self, res):
        try:
            json.loads(res.headers.get('X-ClickHouse-Summary', '{}'))
        except Exception:
            return {}

    def execute(self, sql):
        try:
            res = requests_session.post(
                url=self.url,
                data=sql,
                headers=self.auth,
                verify=settings.YANDEX_ROOT_CERTIFICATE,
            )
        except ConnectionError as e:
            raise ClickHouseConnectionError(e) from e
        except Timeout as e:
            raise ClickHouseTimeoutError(e) from e
        if res.status_code == 400:
            raise ClickHouseQueryError(res.text)
        elif res.status_code != 200:
            raise ClickHouseUnknownError(res.text)
        return res.text

    def query(self, sql):
        result = self.execute(sql)
        for line in result.split('\n'):
            if line:
                yield line.split('\t')


class InsertSql:
    def __init__(self, metadata_class):
        self.table_name = metadata_class.__name__
        self.fields = metadata_class._fields
        self.stream = StringIO()
        self.stream.write('insert into {table_name} ({fields}) values\n'.format(
            table_name=self.table_name,
            fields=', '.join(self.fields),
        ))
        self.rows = 0

    def write(self, row):
        if self.rows > 0:
            self.stream.write(',\n')
        self.stream.write('(')
        self.stream.write(
            ', '.join(
                self._row_to_string(row, field)
                for field in self.fields
            )
        )
        self.stream.write(')')
        self.rows += 1

    def _row_to_string(self, row, field):
        value = getattr(row, field)
        if value is None:
            return 'null'
        elif isinstance(value, datetime):
            return value.strftime("'%Y-%m-%d %H:%M:%S'")
        elif isinstance(value, date):
            return value.strftime("'%Y-%m-%d'")
        elif isinstance(value, bool):
            return '1' if value else '0'
        elif isinstance(value, int):
            return str(value)
        elif isinstance(value, str):
            return self._escape(value)
        else:
            raise TypeError(value)

    def _escape(self, s):
        st = StringIO()
        st.write("'")
        for ch in s:
            if ch == '\\':
                st.write(ch)
                st.write(ch)
            elif ch == "'":
                st.write("\\'")
            else:
                st.write(ch)
        st.write("'")
        return st.getvalue()

    def getvalue(self):
        return self.stream.getvalue().encode()
