# Tg utils v2.22
# 14/06/2021
# https://t.me/ssleg  © 2020 – 2021

import logging
from datetime import datetime

import psycopg2
from telethon import TelegramClient


class PgServer:
    """класс postgres-сервер"""

    __slots__ = ['__con', '__control_flag', '__cursor', '__db_name']

    def __init__(self, dbc=None, db_name='mydb'):
        if dbc is None:
            self.__con = psycopg2.connect(database=db_name,
                                          user='postgres',
                                          password='1234',
                                          host='127.0.0.1',
                                          port='5432')
            self.__control_flag = True
            self.__db_name = db_name
        else:
            self.__con = dbc
            self.__control_flag = False
            self.__db_name = ''

        self.__cursor = self.__con.cursor()

    def commit(self):
        self.__con.commit()

    def rollback(self):
        self.__con.rollback()

    def exec(self, sql, req_data=None, return_type=None, retry_count=0):
        try:
            if req_data is not None:
                self.__cursor.execute(sql, req_data)
            else:
                self.__cursor.execute(sql)

            if return_type is not None:
                if return_type == 0:
                    return self.__cursor.fetchall()
                else:
                    return self.__cursor.fetchone()
            else:
                return True

        except Exception as e:
            levent = 'postgres error: ' + str(e)
            logging.error(levent)
            if self.__control_flag:
                if retry_count < 3:
                    self.__con.close()
                    self.__con = psycopg2.connect(database=self.__db_name,
                                                  user='postgres',
                                                  password='1234',
                                                  host='127.0.0.1',
                                                  port='5432')
                    self.__cursor = self.__con.cursor()

                    return self.exec(sql, req_data, return_type, retry_count=retry_count + 1)

            return False

    def __del__(self):
        self.__cursor.close()
        if self.__control_flag:
            self.__con.close()
        else:
            self.__con.rollback()


class PgLock:
    """класс блокировки базы данных"""

    __slots__ = ['__srv', '__cursor', '__lock_name', '__debug']

    def __init__(self, dbc=None, lck='default_lock', debug=False):
        self.__srv = PgServer(dbc)
        self.__lock_name = lck
        self.__debug = debug

    def lock(self):
        self.__srv.exec('update locks set status=True where lock_name=%s', (self.__lock_name,))
        self.__srv.commit()
        if self.__debug:
            levent = 'locked #' + self.__lock_name
            logging.info(levent)

    def unlock(self):
        self.__srv.exec('update locks set status=False where lock_name=%s', (self.__lock_name,))
        self.__srv.commit()
        if self.__debug:
            levent = 'unlocked #' + self.__lock_name
            logging.info(levent)

    def lock_status(self):
        row = self.__srv.exec('select status from locks where lock_name=%s', (self.__lock_name,), return_type=1)
        self.__srv.rollback()

        if row is not None:
            lock_status = row[0]
            return lock_status
        else:
            return False


class GlobalCounter:
    """глобальный универсальный счетчик"""

    __slots__ = ['__count']

    @staticmethod
    def __is_valid_arg(arg):
        if type(arg) is int:
            if arg > 0:
                return True
        return False

    def __init__(self, init_count=0):
        if GlobalCounter.__is_valid_arg(init_count):
            self.__count = init_count
        else:
            self.__count = 0

    def increment(self, step=1):
        if GlobalCounter.__is_valid_arg(step):
            self.__count += step
            return True
        return False

    def decrement(self, step=1):
        if GlobalCounter.__is_valid_arg(step):
            if self.__count - step >= 0:
                self.__count -= step
                return True
        return False

    def get(self):
        return self.__count

    def set(self, value):
        if GlobalCounter.__is_valid_arg(value):
            self.__count = value
            return True
        return False

    def clear(self):
        self.__count = 0

    def __isub__(self, other):
        if GlobalCounter.__is_valid_arg(other):
            self.__count -= other

    def __iadd__(self, other):
        if GlobalCounter.__is_valid_arg(other):
            self.__count += other

    def __eq__(self, other):
        if self.__count == other:
            return True
        return False

    def __ge__(self, other):
        if self.__count >= other:
            return True
        return False

    def __gt__(self, other):
        if self.__count > other:
            return True
        return False

    def __le__(self, other):
        if self.__count <= other:
            return True
        return False

    def __lt__(self, other):
        if self.__count <= other:
            return True
        return False

    def __str__(self):
        return str(self.__count)


class GlobalFlag:
    """глобальный универсальный флаг"""

    __slots__ = ['__flag']

    def __init__(self):
        self.__flag = False

    def set_true(self):
        self.__flag = True

    def set_false(self):
        self.__flag = False

    def switch(self):
        if self.__flag:
            self.__flag = False
        else:
            self.__flag = True

    def get(self):
        return self.__flag

    def __bool__(self):
        return self.__flag

    def __str__(self):
        return str(self.__flag)


def t_stamp():
    now = datetime.now()
    stamp = datetime.strftime(now, '%d/%m %Y %H:%M:%S')
    return stamp


def t_stamp_sh():
    now = datetime.now()
    stamp = datetime.strftime(now, '%H:%M')
    return stamp


# ловеркейс имени tg канала с проверкой валидности
def set_ch_name_lower(teststr):
    i = 0
    rezult = ''
    while i < len(teststr):
        char = teststr[i]
        code = ord(char)

        if code == 95:
            pass
        else:
            if 47 < code < 58:
                pass
            else:
                if 64 < code < 91:
                    code += 32
                else:
                    if 96 < code < 123:
                        pass
                    else:
                        levent = 'ошибка конвертации канала: ' + str(i) + ' ' + str(code) + ' ' + char + ' ' + teststr
                        logging.warning(levent)
                        return False
        rezult += chr(code)
        i += 1

    return rezult


# сборка имени юзера в читабельное
def set_name_printable(first, last, account=None, phone=None, ad=''):
    name = str(first) + ' '

    if last is not None:
        name = name + str(last) + ' '

    if account is not None:
        name = name + '(' + ad + str(account) + ')' + ' '

    if phone is not None:
        name = name + '+' + str(phone) + ' '

    ind = len(name)
    res = name[0:ind - 1]
    return res


# вывод числа с разделителями тысяч
def set_int_printable(integer, razd=' '):
    string = '{:,}'.format(integer)
    string = string.replace(',', razd)
    return string


# версия сервера постгрес
def get_server_version(dbc=None):
    srv = PgServer(dbc)
    sql = "select setting from pg_config where name='VERSION'"
    row = srv.exec(sql, return_type=1)
    version = row[0]
    return version


# создает обьект клиента с параметрами из бд
def get_tg_client(name, dbc=None, cust_name=None):
    srv = PgServer(dbc)

    n_api = name + '_api_id'
    row = srv.exec('select value from parameters where name=%s', (n_api,), return_type=1)
    api_id = int(row[0])

    n_hash = name + '_api_hash'
    row = srv.exec('select value from parameters where name=%s', (n_hash,), return_type=1)
    api_hash = row[0]

    if cust_name is None:
        client = TelegramClient(name, api_id, api_hash)
    else:
        client = TelegramClient(cust_name, api_id, api_hash)
    return client


# достает ключ бота из бд
def get_bot_key(name, dbc=None):
    srv = PgServer(dbc)

    bot = name + '_key'
    row = srv.exec('select value from parameters where name=%s', (bot,), return_type=1)
    bot_key = row[0]
    return bot_key