class Functions():
    def __init__(self, token):
        self.TOKEN = token
        locale.setlocale(locale.LC_ALL, '')
        self.days = [
            'todos os dias',
            'Segunda-Feira',
            'Terça-Feira',
            'Quarta-Feira',
            'Quinta-Feira',
            'Sexta-Feira',
            'Sábado',
            'Domingo',
        ]
        self.reg = [
            r'^\d*$',  # user_id
            r'^(\d\d[/]){2}\d{4}$',  # date
            r'^[1-9]+\d*$',  # blocks
            r'^\d+([,]\d+)?$',  # perc, capital
            r'^([a-zA-Z]{4}\d{1,2}|B3SA3)(, ?([a-zA-Z]{4}\d{1,2}|B3SA3))*?$',  # tickers
            r'^([0-1]\d|2[0-3]):[0-5]\d$',  # hour
        ]
        self.modes = ['SD', 'SW', 'MD', 'MW']
        self.sm_dw = {
            self.modes[0]: 'Small Caps/Diário',
            self.modes[1]: 'Small Caps/Semanal',
            self.modes[2]: 'Mid Large Caps/Diário',
            self.modes[3]: 'Mid Large Caps/Semanal',
        }
        self.rd = Radar()

    def func_send_msg(self,
                      chat_id,
                      text,
                      reply_to_message_id='',
                      reply_markup=''):
        url = 'https://api.telegram.org/bot{}/sendMessage?' \
            'chat_id={}&text={}&reply_to_message_id={}&' \
            'reply_markup={}&parse_mode=HTML'
        requests.get(
            url.format(self.TOKEN, chat_id, text, reply_to_message_id,
                       reply_markup))

    def schedule(self):
        print('Scheduling user reports...')
        users = db.get_everything('users')
        if users:
            for user in users:
                if user[4]:
                    hour = self.rd.hour_fix(user[9])
                    kwargs = {
                        'user_id': user[0],
                        's_m': user[7],
                        'd_w': user[8],
                    }
                    self.rd.weekly(hour, self.func_radar_auto, str(user[0]),
                                   **kwargs)
        else:
            pass
        print('Reports scheduled.')

    def func_time(self, var, separator):
        if separator == '/':
            new_format = dtt.strptime(var, f'%d/%m/%Y').strftime(f'%Y-%m-%d')
        elif separator == '-':
            new_format = dtt.strptime(var, '%Y-%m-%d').strftime('%d/%m/%Y')
        return new_format

    def func_user_start(self, user_id, name, username):
        exists = db.get_info(user_id)
        if exists:
            admin_text = f'Usuário mandou novamente:\nuser_id: {user_id}\n' \
                         f'nome: {name}\nusername: @{username}'
            user_text = 'Você já deu o start! Algum problema? Pergunte ao @DanMMoreira!'
        else:
            dia = str(dtt.now().date())
            db.user_start(user_id, name, username, dia)
            admin_text = f'Novo usuário:\nuser_id: {user_id}\n' \
                         f'nome: {name}\nusername: @{username}'
            user_text = 'Bem-vindo, investidor! Por favor, aguarde até ser autorizado.'
        return admin_text, user_text

    def func_admin(self, msg, choice):
        info = msg.split(', ')
        # db.admin_queries(info_0, info_1, info_2, choice)
        # 0 se autoriza: info_0 = user_id, info_1 = full_name, info_2 = dia de hoje;
        # 1 se desativa: info_0 = user_id, info_1 = info_2 = '';
        # 2 se edita:    info_0 = user_id, info_1 = campo, info_2 = novo dado;
        # 3 se pesquisa: info_0 = campo, info_1 = pesquisa;
        # 4 pesquisa por data: info_0 = data inicial, info_1 = data final
        # 5 se reseta:   info_0 = user_id
        # The query:
        try:
            if choice < 3:
                y = re.match(self.reg[0], info[0])
                if not y:
                    response = 'Formato inválido! O user_id deve conter somente números. Tente novamente.'
                    return response, False
            if choice == 0:
                date = str(dtt.now().date())
                res = db.admin_queries(info[0], info[1], date, choice)
                action = 'autorizado'
            elif choice in {1, 5}:
                res = db.admin_queries(info[0], choice=choice)
                action = 'desativado' if choice == 1 else 'resetado'
            elif choice in {2, 3, 4}:
                if ((choice == 2 and re.match('^(nome|username|email)$', info[1], re.I)) \
                        or (choice == 3 and re.match('^(user_id|nome|username)$', info[0], re.I))):
                    if re.match('^nome$', info[0], re.I):
                        info[0] = info[0].replace('o', 'a')
                    if re.match('^nome$', info[1], re.I):
                        info[1] = info[1].replace('o', 'a')
                    res = db.admin_queries(info[0], info[1], choice=choice)

                elif (choice == 4 and re.match(self.reg[1], info[0]) \
                        and re.match(self.reg[1], info[1])):
                    info[0] = self.func_time(info[0], '/')
                    info[1] = self.func_time(info[1], '/')
                    res = db.admin_queries(info[0], info[1], choice=choice)
            # The response:
            if res == 0:
                response = 'Este usuário não existe! Tente novamente ou clique em /cancelar.'
                return response, False
            elif not res:
                response = 'Nada foi encontrado para essa pesquisa! Tente novamente ou clique em /cancelar.'
                return response, False
            elif choice in {0, 1, 5}:
                if res == 1:
                    response = f'Este usuário já está {action}!'
                else:
                    response = f'O usuário foi {action} com sucesso!'
            elif choice == 2:
                response = 'O campo foi editado com sucesso!'
            elif choice in {3, 4}:
                response = 'Resultados da pesquisa:\n\r\n\r'
                for item in res:
                    if item[3] == None: item[3] = '-'
                    item[4] = 'Desativado' if item[4] == '0' else 'Autorizado'
                    item[5] = self.func_time(item[5], '-')
                    response += (
                        f'user_id: {item[0]}\n\r'
                        f'Nome: {item[1]}\n\r'
                        f'username: {item[2]}\n\r'
                        f'E-mail: {item[3]}\n\r'
                        f'{item[4]}\n\r'
                        f'Data de entrada / última autorização: {item[5]}\n\r\n\r'
                    )
            return response, True
        except Exception as e:
            print(e)
            response = 'Formato inválido! Esqueceu algo? Tente novamente ou clique em /cancelar.'
            return response, False

    def func_init_check(self, mode, data, user_id='', all_data=''):
        if mode == 'B' and not re.match(self.reg[2], data):
            text = 'Formato inválido. Digite o valor dos bloquinhos neste formato ' \
                '(somente números, sem aspas): "8" ou "12". Tente novamente:'
            return text, False
        elif mode == 'P' and not re.match(self.reg[3], data):
            text = 'Formato inválido. Digite o valor do porcentual neste formato ' \
                '(somente números, sem aspas): "1,5" ou "2". Tente novamente:'
            return text, False
        elif mode in ['B', 'P']:
            text = 'Por último: qual o seu capital atual para investir pelo método ' \
                '(ex.: "1234,56", sem as aspas)?\r\n' \
                'Sem o capital, o gerenciamento de risco não funcionará, mas envie "0"'\
                ' (zero, sem as aspas) se não quiser responder. Ele serve para o cálculo ' \
                'automático do volume a ser comprado.'
            return text, True
        elif mode == 'p':
            if not re.match(self.reg[3], data):
                text = 'Formato inválido. Digite o valor do capital neste formato (somente números, sem aspas): ' \
                    '"1234,56" ou digite "0" se não quiser responder. Tente novamente:'
                return text, False
            else:
                hour = self.rd.hour_fix('10:30')
                kwargs = {
                    'user_id': user_id,
                    's_m': all_data[0],
                    'd_w': all_data[1],
                }
                self.rd.weekly(hour, self.func_radar_auto, str(user_id),
                               **kwargs)
                text = 'Tudo pronto!\nVocê irá receber relatórios diários conforme suas configurações ' \
                    'às 10:30. ' \
                    'Você pode mudar todas essas configurações através do /menu e ' \
                    'lá você também pode obter o relatório manualmente. Para saber mais informações ' \
                    'sobre o bot ou se precisa de ajuda, contate o desenvolvedor @DanMMoreira. ' \
                    'Este bot ainda está em fase de testes, portanto algumas coisas podem estar... ' \
                    'esquisitas. Mas muitas coisas estão por vir!\nAproveite!' # Se escolheu a escala Semanal, receberá apenas às segundas-feiras.
                return text, True

    def func_get_info(self, user_id):
        info = db.get_info(user_id)
        stock = 'Small Caps' if info[0][7] == 'S' else 'Mid Large Caps'
        freq = 'Diário' if info[0][8] == 'D' else 'Semanal'
        #day = self.days[0] if info[0][8] == 'D' else self.days[int(info[0][10])]
        if info[0][11] == 'B':
            risk = 'Bloquinho por operação\n\rBloquinhos: ' + info[0][12] + ''
        else:
            risk = 'Porcentagem relativa ao stop\n\rPorcentual: ' + info[0][
                12] + '%'
        text = 'MEU STATUS:\n\r\n\r' \
            'Capital: R$'+info[0][6]+'\n\r' \
            'Classe de ações padrão: '+stock+'\n\r' \
            'Escala temporal padrão: '+freq+'\n\r' \
            'Hora programada: ' \
            ''+info[0][9]+'\n\r' \
            'Gerenciamento de risco: '+risk #Hora e dia da semana programados:info[], day
        return text

    def func_portf_upd(self, user_id, msg, choice):
        if choice < 3 and re.match(self.reg[3], msg):
            if msg.rfind(','): msg = msg.replace(',', '.')
            msg = float(msg)
            portf = db.get_info(user_id)[0][6]
            if portf.rfind(','): portf = portf.replace(',', '.')
            portf = float(portf)
            if choice == 0: portf += msg
            if choice == 1: portf -= msg
            if choice == 2: portf = msg
            portf = '{:.2f}'.format(portf).replace('.', ',')
            db.info_upd(user_id, 'portf', portf)
            text = 'Pronto! O novo capital é de R$' + portf + '.\n\rAté mais!'
            return text, True
        elif choice == 3 and re.match('^(Sim)$', msg):
            portf = '0'
            db.info_upd(user_id, 'portf', portf)
            text = 'Pronto! O capital foi zerado.\n\rAté mais!'
            return text
        else:
            text = 'Formato inválido. Tente colocar o valor neste formato ' \
                '(somente números): "1234,56" ou clique em /fechar:'
            return text, False

    def func_get_tickers_user(self, user_id, show_only=True):
        t_list = db.get_everything(user_id)
        if t_list:
            if show_only:
                text = ''
                for m in self.modes:
                    tickers = [x[0] for x in t_list if x[1] == m]
                    if tickers:
                        text += '-- ' + self.sm_dw[m] + ' --\n' + '\n'.join(
                            tickers) + '\n\n'
            else:
                text = [x[0] for x in t_list]
        else:
            text = 'A sua carteira está vazia!'
        return text

    def func_tickers_upd_user(self, user_id, msg, choice):
        if re.match(self.reg[4], msg):
            tickers = map(lambda x: x.upper(), re.split(r'\W+', msg))
            success = db.tickers_upd_user(user_id, tickers, choice)
            action = [
                'Você adicionou o(s) ativo(s) ' + msg +
                ' com sucesso!\nAté mais!',
                'O ativo ' + msg + ' foi removido com sucesso!\nAté mais!'
            ]
            if success:
                text = action[0] if choice < 4 else action[1]
            else:
                text = 'Não existe este ativo na sua carteira!\nAté mais!'
        else:
            success = False
            text ='Tente colocar o índice neste formato: "PETR4" ' \
                '(sem as aspas) ou clique em /fechar:'
        return text, success

    def func_time_upd(self, user_id, choice):
        time = db.get_info(user_id)[0][9 + choice]
        if choice == 0:
            cb_text = 'MUDAR HORA\r\n' \
                'A hora programada atual é '+time+'. Digite a nova hora ' \
                'desejada ou selecione uma das opções:'
            text, keyboard = False, False
        else:
            d_w = db.get_info(user_id)[0][8]
            if d_w == 'D':
                cb_text = 'A escala programada no seu perfil é Diário, portanto ' \
                    'você receberá mensagens todos os dias. Para receber mensagens ' \
                    'apenas 1 vez por semana, você deve mudar a escala para ' \
                    'Semanal, em Menu > Configurações > Configurações de Modo.\r\n' \
                    'Selecione uma das opções:'
                text, keyboard = False, False
            else:
                cb_text = 'MUDAR DIA\r\n' \
                    'O dia programado atual é '+self.days[int(time)]+'.'
                text = 'Escolha abaixo o novo dia desejado:'
                keyboard = [[x] for x in self.days if not x == self.days[0]]
        return cb_text, text, keyboard

    def func_time_exit(self, user_id, choice, msg):
        if choice == 0:
            if re.match(self.reg[5], msg):
                user = db.admin_queries('user_id', str(user_id), choice=3)
                hour = self.rd.hour_fix(msg)
                kwargs = {
                    'user_id': user_id,
                    's_m': user[0][7],
                    'd_w': user[0][8],
                }
                schedule.clear(str(user_id))
                self.rd.weekly(hour, self.func_radar_auto, str(user_id),
                               **kwargs)
                db.info_upd(user_id, 'hour', msg)
                text = 'A hora programada foi atualizada com sucesso!\r\nAté mais!'
                success = True
            else:
                text = 'Tente colocar a hora neste formato: "06:18", "13:45" ' \
                    '(sem as aspas) ou clique em /fechar::'
                success = False
        else:
            if re.match('^' + '|'.join(self.days) + '$', msg):
                data = str(self.days.index(msg))
                db.info_upd(user_id, 'r_day', data)
                text = 'O dia programado foi atualizado com sucesso!\r\nAté mais!'
                success = True
            else:
                text = 'Você deve selecionar o dia da lista. Tente novamente:'
                success = False
        return text, success

    def func_mode_upd(self, user_id, choice):
        change = choice.split(',')
        choice = choice.replace(',', '')
        user = db.admin_queries('user_id', str(user_id), choice=3)
        hour = self.rd.hour_fix(user[0][9])
        kwargs = {
            'user_id': user_id,
            's_m': change[0],
            'd_w': change[1],
        }
        schedule.clear(str(user_id))
        self.rd.weekly(hour, self.func_radar_auto, str(user_id), **kwargs)
        db.info_upd(user_id, 'S_M', change[0])
        db.info_upd(user_id, 'D_W', change[1])
        text = 'O modo foi atualizado com sucesso! As mensagens automáticas de radar ' \
            'possuirão classe de ações e escala ' \
            'equivalentes a '+self.sm_dw[choice]+'.\r\nAté mais!'
        return text

    def func_risk_upd(self, user_id, choice):
        db.info_upd(user_id, 'B_P', choice)
        if choice == 'B':
            text = 'Agora, digite o número de bloquinhos que serão utilizados (ex.: "8", sem as aspas):'
        else:
            text = 'Agora, digite o porcentual de risco (ex.: "1,5", sem as aspas):'
        return text

    def func_risk_exit(self, user_id, choice, msg):
        if choice == 'B' and not re.match(self.reg[2], msg):
            text = 'Formato inválido. Digite o valor dos bloquinhos neste formato ' \
                '(somente números, sem aspas): "8" ou "12". Tente novamente ou clique em /fechar:'
            success = False
        elif choice == 'P' and not re.match(self.reg[3], msg):
            text = 'Formato inválido. Digite o valor do porcentual neste formato ' \
                '(somente números, sem aspas): "1,5" ou "2". Tente novamente ou clique em /fechar:'
            success = False
        else:
            db.info_upd(user_id, 'B_P_set', msg)
            text = 'Pronto! Seu gerenciamento de risco já está configurado.\r\nAté mais!'
            success = True
        return text, success

    def func_radar(self, choice, user_id, mode, auto=False):
        if choice == 'buy':
            info = db.get_info(user_id)
            hour = info[0][9].replace(':', '-')
            portf = info[0][6]
            if portf.rfind(','): portf = portf.replace(',', '.')
            portf = float(portf)
            b_p = info[0][11]
            b_p_set = info[0][12]
            if b_p_set.rfind(','): b_p_set = b_p_set.replace(',', '.')
            b_p_set = float(b_p_set)
            order = 0 if info[0][13] == None else int(info[0][13])
            m = self.modes[int(mode)]
            stocks = self.rd.trigger_buy(m, portf, b_p, b_p_set, auto, hour)
            if stocks == []:
                text = 'No momento, nenhum ativo está perto de romper o canal superior.\r\nRelaxe!'
            else:
                if order: stocks.sort(key=lambda x: x[order])
                text = '  | ------ Compra - '+self.sm_dw[m]+' ------ | \r\n' \
                    'Ação | Vol | Sup | Fech | Inf\r\nDist | Trend\r\n' \
                    '<i>!!Os ativos em atenção romperam o canal superior nos ' \
                    'últimos 3 dias! Cuidado!!\r\n\r\n</i>'
                for item in stocks:
                    item[2] = locale.format('%1.2f', item[2], 1)
                    item[3] = locale.format('%1.2f', item[3], 1)
                    item[4] = locale.format('%1.2f', item[4], 1)
                    item[5] = '{:.2f}'.format(item[5])
                    item[6] = '{:.2f}'.format(item[6])
                    text_A = f'{item[0]} | {item[1]} | ${item[2]} | ${item[3]} | ${item[4]}\r\n'
                    text_B = f'{item[5]}% | {item[6]}'
                    if item[7]:
                        text += '!!' + text_A + '<i>' + text_B + '!!!!</i>\r\n\r\n'
                    else:
                        text += text_A + text_B + '\r\n\r\n'
        elif choice == 'track':
            t_list = db.get_everything(user_id)
            if t_list:
                t_gen = self.rd.trigger_track(t_list)
                text = ' | ------ Carteira ------ | \r\n' \
                    'Ação | Fech | Inf -> Inf(novo)\r\n\r\n'
                for group, m in t_gen:
                    text += '---- ' + self.sm_dw[m] + ' ----\r\n\r\n'
                    for item in group:
                        if item[1]:
                            item[1] = locale.format('%1.2f', item[1], 1)
                            item[2] = locale.format('%1.2f', item[2], 1)
                            text_A = f'{item[0]} | ${item[1]}'
                            if len(item) == 4:
                                item[3] = locale.format('%1.2f', item[3], 1)
                                text += f'<i>!!{text_A} | ${item[3]} -> ${item[2]}!!</i>\r\n\r\n'
                            else:
                                text += f'{text_A} | ${item[2]}\r\n\r\n'
                        else:
                            text += f'{item[0]} | dados não encontrados - ' \
                                'ativo incorreto ou muito novo.'
                    text += '\r\n'
            else:
                text = 'A sua carteira está vazia! Acesse /menu > Carteira para adicionar ativos.'
        return text

    def func_radar_auto(self, user_id, s_m, d_w):
        if s_m == None or d_w == None:
            pass
        else:
            mode = s_m + d_w
            mode = self.modes.index(mode)
            buy_text = self.func_radar('buy', user_id, mode, True)
            track_text = self.func_radar('track', user_id, mode)
            self.func_send_msg(user_id, buy_text)
            self.func_send_msg(user_id, track_text)

    def func_order(self, user_id, order=False):
        orders = [[
            'Índice do ativo (Ação)', 'Volume (vol)', 'Canal superior (Sup)',
            'Último fechamento (Fech)', 'Canal inferior (Inf)',
            'Distância (Dist)', '"Trendabilidade" (Trend)'
        ], [
            '"Ação"', '"Vol"', '"Sup"', '"Fech"', '"Inf"', '"Dist"', '"Trend"'
        ]]
        if not order:
            info = db.get_info(user_id)
            if info[0][13] == None:
                text = orders[0][0]
            else:
                text = orders[0][int(info[0][13])]
        else:
            db.info_upd(user_id, 'sorting', order)
            text = orders[1][int(order)]
        return text