import re
import os
import requests
import time
import datetime
from slacker import Slacker
from utils import Utils


status_bar = Utils()


class SlackerGraber(object):
    def __init__(self, token):
        self.slacker = Slacker(token)

    def get_channels_id(self, chanls):
        """
        function gets id of given channels in params,
        refer to documentation below
        https://api.slack.com/methods/channels.list
        :param chanls: names of channels
        :return: ids of given channels
        """

        channels = self.slacker.channels.list().body
        ids = {channel['name']: channel['id'] for channel in channels[
            'channels'] if channel['name'] in chanls}
        if ids:
            return ids

    def get_users_id(self, users):
        """
        function gets id of given users in params,
        refer to documentation below
        https://api.slack.com/methods/users.list
        :param users: usernames
        :return: ids of given users
        """
        channel_users = self.slacker.users.list().body
        ids = {user['id']: user['name'] for user in channel_users[
            'members'] if user['name'] in users}
        if ids:
            return ids

    def get_messages(self, channel, users, timestamp, count=1000):
        """
        function retrieves messages of user from channel history
        refer to documentation below
        https://api.slack.com/methods/channels.history
        :param channel: slack channel (id)
        :param users: slack user (id)
        :param timestamp: earliest message to retrieve
        :param count: max number of message to retrieve
        :return: list of messages except file-messages
        """

        def timestamp_to_date(stamp):
            """
            function converts timestamp to "humanized" date
            :param stamp: timestamp
            :return: formatted date
            """
            return datetime.datetime.fromtimestamp(
                int(float(stamp))
            ).strftime('%d.%m.%Y')

        slack_messages = self.slacker.channels.history(channel=channel,
                                                       oldest=timestamp,
                                                       count=count).body
        messages = ['{} - {} - {}'.format(
            timestamp_to_date(message['ts']), users[message['user']],
            message[
                'text']) for message in slack_messages['messages'] if
            message.get('user') in users and
            'a file:' not in message.get('text')]
        if messages:
            return messages

    def get_file_links(self, channel, user, timestamp, count=1000):
        """
        function retrieves links of files, extentions, filenames
        from given channel and user
        refer to doumentation below
        https://api.slack.com/methods/files.list
        :param channel: slack channel (id)
        :param user: slack user (id)
        :param timestamp: earliest file to retrieve
        :param count: max number of files to retrieve
        :return: list with filename, url link of the file and extension
        of the file
        """
        slack_files = self.slacker.files.list(user=user, ts_from=timestamp,
                                              count=count).body
        files = [[file['name'].split('.')[0], file.setdefault(
            'url_private_download', None), file[
                      'filetype']] for file
                 in slack_files['files'] if channel in file['channels']]
        if files:
            return files

    @staticmethod
    def date_to_timestamp(date):
        """
        function converts "humanized" date to timestamp
        :param date: date
        :return: timestamp 
        """
        regex = re.compile(r'\W')
        _date = regex.sub('.', date)
        return time.mktime(datetime.datetime.strptime(
            _date, '%d.%m.%Y').timetuple())


class Downloader(object):
    def __init__(self, path, filename, url, extension, headers):
        try:
            self.url = requests.get(url, headers=headers, stream=True)
            self.path = path
            self.filename = filename
            self.extension = extension
            self.errors = []
        except ValueError:
            self.url = None
            self.filename = filename

    def donwload_file(self, chunk_size=1024 * 8, status=None):
        """
        function downloads file from slack channels
        :param chunk_size: size of chunk to download
        :param status: if True progress of file download print to terminal
        :return: None if error occurs, 1 if download was successful
        """
        if self.url is None:
            error = 'unable to download file {}'.format(self.filename)
            print(error)
            self.errors.append(error)
            return
        if not os.path.isdir(self.path):
            raise OSError('invalid path, does not exist')
        if self.extension == 'space':
            self.extension = 'txt'
        filename = '{}.{}'.format(self.filename, self.extension)
        path = os.path.join(self.path, filename)
        if os.path.isfile(path):
            error = '{} already exist'.format(self.filename)
            print(error)
            self.errors.append(error)
            return
        file_size = self.read_file_size(self.url)
        if file_size is None:
            error = 'unable to download file {},' \
                    ' unable to get file size'.format(
                self.filename)
            print(error)
            self.errors.append(error)
            return
        start = time.time()
        bytes_received = 0
        try:
            with open(path, 'wb') as result_file:
                for chunk in self.url.iter_content(chunk_size):
                    bytes_received += len(chunk)
                    result_file.write(chunk)
                    if status:
                        status_bar.progress_bar(completion=bytes_received,
                                                total=int(file_size),
                                                start_time=start)
        except KeyboardInterrupt:
            os.remove(path)
            raise KeyboardInterrupt('download interupted by user')
        return 1

    @staticmethod
    def read_file_size(response):
        meta_data = dict(response.headers.items())
        return meta_data.get('Content-Length') or \
               meta_data.get('content-length')


class MessagesWriter(object):
    def __init__(self, path, channel_name, messages):
        self.path = path
        self.messages = messages
        self.channel_name = channel_name

    def write_messages(self, timestamp=None):
        """
        function writes text from a list to the file
        :param timestamp: optional param to include in filename
        :return: None
        """
        if not os.path.isdir(self.path):
            raise OSError('invalid path, does not exist')
        if timestamp:
            timestamp = time.time()
            dest_path = os.path.join(self.path,
                                     str(timestamp) + ' ' +
                                     self.channel_name + ' messages.txt')
        else:
            dest_path = os.path.join(self.path, self.channel_name +
                                     ' messages.txt')
        with open(dest_path, 'w') as dest_file:
            dest_file.writelines(self.messages)
