import discord
from discord.ext import commands

from geeksbot.imports.utils import Paginator, Book


class Tickets(commands.Cog):
    def __init__(self, bot):
        self.bot = bot

    @commands.command()
    async def request(self, ctx, *, message=None):
        if not ctx.guild:
            await ctx.send('This command must be run from inside a guild.')
            return

        if not message:
            await ctx.send('Please include a message containing your request')
            return

        if len(message) > 1000:
            await ctx.send('Request is too long, please keep your request to less than 1000 characters.')
            return

        data = {
            'author': ctx.author.id,
            'message': ctx.message.id,
            'channel': ctx.channel.id,
            'content': message
        }
        msg_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/messages/{ctx.message.id}/wait/', headers=self.bot.auth_header)
        if msg_resp.status == 404:
            error = await msg_resp.json()
            await ctx.send(error['details'])
            return

        resp = await self.bot.aio_session.post(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/', headers=self.bot.auth_header, json=data)

        if resp.status == 201:
            admin_channel_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/channels/{ctx.guild.id}/admin/', headers=self.bot.auth_header)
            request = await resp.json()

            if admin_channel_resp.status == 200:
                admin_chan_data = await admin_channel_resp.json()
                msg = f''
                admin_roles_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/guilds/{ctx.guild.id}/roles/admin/', headers=self.bot.auth_header)
                if admin_roles_resp.status == 200:
                    admin_roles_data = await admin_roles_resp.json()
                    for role in admin_roles_data:
                        msg += f'{ctx.guild.get_role(int(role["id"])).mention} '
                msg += f"New Request ID: {request['id']} " \
                       f"{ctx.author.mention} has requested assistance: \n" \
                       f"```{request['content']}``` \n" \
                       f"Requested at: {request['requested_at'].split('.')[0].replace('T', ' ')} GMT\n" \
                       f"In {ctx.guild.get_channel(int(request['channel'])).name}"
                admin_chan = ctx.guild.get_channel(int(admin_chan_data['id']))
                await admin_chan.send(msg)
            await ctx.send(f'{ctx.author.mention} The admin have received your request.\n'
                           f'If you would like to update or close your request please reference Request ID `{request["id"]}`')

    @commands.command(aliases=['comment'])
    async def update(self, ctx, request_id=None, *, comment: str = None):
        try:
            request_id = int(request_id)
        except ValueError:
            await ctx.send("Please include the ID of the request you would like to update as the first thing after the command.")
            return

        if not comment:
            await ctx.send("There is nothing to update since you didn't include a message.")
            return

        data = {
            'author': ctx.author.id,
            'content': comment
        }

        comment_resp = await self.bot.aio_session.post(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/{request_id}/comments/', headers=self.bot.auth_header, json=data)

        if comment_resp.status == 201:
            comment = await comment_resp.json()
            admin_channel_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/channels/{ctx.guild.id}/admin/',
                                                                headers=self.bot.auth_header)

            if admin_channel_resp.status == 200:
                admin_channel_data = await admin_channel_resp.json()
                admin_channel = ctx.guild.get_channel(int(admin_channel_data['id']))
                if admin_channel:
                    request_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/{request_id}/', headers=self.bot.auth_header)
                    pag = Paginator(self.bot, prefix='```md', suffix='```')
                    header = f'{ctx.author.mention} has commented on request {request_id}\n'
                    if request_resp.status == 200:
                        request = await request_resp.json()
                        requestor = ctx.guild.get_member(int(request["author"]))
                        header += (f'Original Request by {requestor.mention if requestor else "`User cannot be found`"}:\n'
                                   f'```{request["content"]}```')
                        pag.set_header(header)

                        if request.get('comments'):
                            comments = request['comments']
                            for comment in comments:
                                author = ctx.guild.get_member(int(comment['author']))
                                pag.add(f'{author.display_name}: {comment["content"]}', keep_intact=True)
                        if ctx.author != requestor and requestor:
                            for page in pag.pages(page_headers=False):
                                await requestor.send(page)
                    book = Book(pag, (None, admin_channel, self.bot, ctx.message))
                    await book.create_book()
            await ctx.send(f'{ctx.author.mention} Your comment has been added to the request.')

    @commands.command(name='requests_list', aliases=['rl'])
    async def _requests_list(self, ctx, closed: str = ''):
        pag = Paginator(self.bot)
        admin_roles_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/guilds/{ctx.guild.id}/roles/admin/', headers=self.bot.auth_header)
        if admin_roles_resp.status == 200:
            admin_roles_data = await admin_roles_resp.json()
            admin_roles = [ctx.guild.get_role(int(role['id'])) for role in admin_roles_data]
            if any([role in ctx.author.roles for role in admin_roles]):
                requests_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/', headers=self.bot.auth_header)
                if requests_resp.status == 200:
                    requests_data = await requests_resp.json()
                    requests_list = requests_data['requests'] if isinstance(requests_data, dict) else requests_data
                    while isinstance(requests_data, dict) and requests_data.get('next'):
                        requests_resp = await self.bot.aio_session.get(
                            requests_data['next'], headers=self.bot.auth_header)
                        if requests_resp.status == 200:
                            requests_data = await requests_resp.json()
                            requests_list.extend(requests_data['requests'] if isinstance(requests_data, dict) else requests_data)
                    for request in requests_list:
                        member = discord.utils.get(ctx.guild.members, id=int(request['author']))
                        title = (f"<{'Request ID':^20} {'Requested By':^20}>\n"
                                 f"<{request['id']:^20} {member.display_name if member else 'None':^20}>")
                        orig_channel = ctx.guild.get_channel(int(request.get('channel')))
                        comments_count_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/{request["id"]}/comments/count/', headers=self.bot.auth_header)
                        pag.add(f"\n\n{title}\n"
                                f"{request['content']}\n\n"
                                f"Comments: {await comments_count_resp.text() if comments_count_resp.status == 200 else 0}\n"
                                f"Requested at: "
                                f"{request['requested_at'].split('.')[0].replace('T', ' ')} GMT\n"
                                f"In {orig_channel.name if orig_channel else 'N/A'}", keep_intact=True)
                    pag.add(f'\n\uFFF8\nThere are currently {len(requests_list)} requests open.')
                else:
                    pag.add('There are no open requests for this guild.', keep_intact=True)
            else:
                requests_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/user/{ctx.author.id}/',
                                                               headers=self.bot.auth_header)
                if requests_resp.status == 200:
                    requests_data = await requests_resp.json()
                    requests_list = requests_data['requests'] if isinstance(requests_data, dict) else requests_data
                    while isinstance(requests_data, dict) and requests_data.get('next'):
                        requests_resp = await self.bot.aio_session.get(
                            requests_data['next'], headers=self.bot.auth_header)
                        if requests_resp.status == 200:
                            requests_data = await requests_resp.json()
                            requests_list.extend(
                                requests_data['requests'] if isinstance(requests_data, dict) else requests_data)
                    for request in requests_list:
                        title = (f"<{'Request ID':^20}>\n"
                                 f"<{request['id']:^20}>")
                        orig_channel = ctx.guild.get_channel(int(request.get('channel')))
                        comments_count_resp = await self.bot.aio_session.get(
                            f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/{request["id"]}/comments/count/',
                            headers=self.bot.auth_header)
                        pag.add(f"\n\n{title}\n"
                                f"{request['content']}\n\n"
                                f"Comments: {await comments_count_resp.text() if comments_count_resp.status == 200 else 0}\n"
                                f"Requested at: "
                                f"{request['requested_at'].split('.')[0].replace('T', ' ')} GMT\n"
                                f"In {orig_channel.name if orig_channel else 'N/A'}", keep_intact=True)
                    pag.add(f'\n\uFFF8\nYou currently have {len(requests_list)} requests open.')
                else:
                    pag.add('You have no open requests for this guild.', keep_intact=True)
        for page in pag.pages():
            await ctx.send(page)

    @commands.command()
    async def close(self, ctx, *, ids=None):
        if not ids:
            await ctx.send('Please include at least one Request ID to close.')
            return

        admin = False
        admin_roles_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/guilds/{ctx.guild.id}/roles/admin/',
                                                          headers=self.bot.auth_header)
        if admin_roles_resp.status == 200:
            admin_roles_data = await admin_roles_resp.json()
            admin_roles = [ctx.guild.get_role(int(role['id'])) for role in admin_roles_data]
            if any([role in ctx.author.roles for role in admin_roles]):
                admin = True

        ids = [id.strip() for id in ids.replace(' ', '').split(',')]

        for id in ids:
            request_resp = await self.bot.aio_session.get(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/{id}/', headers=self.bot.auth_header)
            if request_resp.status == 200:
                request = await request_resp.json()
                requestor = ctx.guild.get_member(int(request['author']))
                if requestor == ctx.author or admin:
                    data = {
                        'completed_by': ctx.author.id
                    }
                    delete_resp = await self.bot.aio_session.delete(f'{self.bot.api_base}/messages/{ctx.guild.id}/requests/{id}/', headers=self.bot.auth_header, json=data)
                    if delete_resp.status == 202:
                        delete_data = await delete_resp.json()
                        if delete_data['completed']:
                            await ctx.send(f'Request {id} closed.')
                            await requestor.send(f'{ctx.author.display_name} has closed request {id} which was '
                                                 f'opened by you in the '
                                                 f'{ctx.guild.get_channel(int(request["channel"])).name} '
                                                 f'channel.'
                                                 f'```{request["content"]}```'
                                                 f'If there are any issues please open a new request.')
                else:
                    await ctx.send('That is not your request to close.')


def setup(bot):
    bot.add_cog(Tickets(bot))
