import json
import logging
import os
import random
import re
import shutil
from datetime import datetime

import discord
import numpy as np
from discord.ext import commands, tasks

from ai_functions import get_random_cyclic_message
from constants import (
    ENCODING,
    LAST_SPONTANEOUS_CALL,
    LOGFILE,
    LOGSTORE,
    MEMORY_FIVE_MUZYKA,
    MEMORY_FIVE_SIARA,
    TIME_BETWEEN_CALLS,
)


class AdministrationModule(commands.Cog):
    def __init__(self, bot, logger_name):
        self.bot = bot
        self.logger = logging.getLogger(logger_name)

    @commands.hybrid_command(
        name="galeria_slaw",
        description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj",
        guild=discord.Object(id=664789470779932693),
    )
    @commands.has_any_role('Nocna Zmiana', 'Jarl', 'Thane' , 'Bartender')
    async def galeria_slaw(self, ctx):
        if isinstance(ctx.channel, discord.DMChannel):
            for guild in self.bot.guilds:
                async for entry in guild.bans(limit=1500):
                    fnord = discord.File(
                        "/home/pi/Conjurer/niech_spierdala.png",
                        spoiler=False,
                        description="Niech spierdala",
                    )
                    await ctx.reply(
                        f"User: {entry.user} wyjebany z serwera {guild.name} za {entry.reason} ",
                        file=fnord,
                    )

    @commands.hybrid_command(
        name="update_banlist",
        description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj",
        guild=discord.Object(id=664789470779932693),
    )
    @commands.has_any_role('Jarl', 'Thane')

    async def update_banlist(self, ctx):
        """
        Update a banlist in a Discord guild from preprovided list in channel.

        :param ctx: ctx stands for "context" and is a parameter commonly used in Discord.py commands. It
        contains information about the context in which the command was invoked, such as the message, the
        channel, the server, and the user who invoked the command. This information can be used to perform
        various actions, such
        """
        async with ctx.channel.typing():
            allowed = False
            for role in ctx.author.roles:
                if role.name == "Jarl":
                    allowed = True

            if ctx.channel.id != 1102190666827702342:
                # trunk-ignore(codespell/misspelled)
                await ctx.send("Nie wydaje mnie sie")
            elif not allowed:
                await ctx.send("Idź bo Cię zdziele")
            else:
                tobancunter = 0
                async with ctx.typing():
                    # 1. get list of users posted on channel for banning
                    channel = ctx.channel
                    messages = [
                        message async for message in channel.history(limit=None)
                    ]
                    ids_to_ban = []
                    for message in messages:
                        lines = message.content.split("\n")
                        for line in lines:
                            match = re.search(
                                "\\d{18}",
                                line,
                                # trunk-ignore(codespell/misspelled)
                            )  # poprawilem backslash na podwojny bo linter sie czepial
                            if match:
                                ids_to_ban.append(match.group())
                                tobancunter += 1
                    # 2. get list of already banned users for all servers Conjurer
                    # has rights to
                    for guild in self.bot.guilds:
                        bancunter = 0
                        # trunk-ignore(codespell/misspelled)
                        self.logger.info("Serwer: %s", guild)
                        current_banlist = []
                        permission = True
                        try:
                            async for entry in guild.bans(limit=None):
                                current_banlist.append(entry.user.id)
                                bancunter += 1
                        except BaseException:  # pylint: disable=broad-exception-caught
                            permission = False
                        if permission:
                            # 3. compare lists ban only users on list 2 and not on
                            # list 1
                            cunter_counter = 0
                            bad_id_cunter_counter = 0
                            ban_list = np.setdiff1d(ids_to_ban, current_banlist)
                            for ident in ban_list:
                                try:
                                    tmp_user = await self.bot.fetch_user(int(ident))
                                    await guild.ban(
                                        tmp_user,
                                        reason="Automatyczna lista banów",
                                        delete_message_seconds=0,
                                    )
                                    # TODO: Maybe use guild.bulk_ban ?
                                    cunter_counter += 1
                                except discord.NotFound:
                                    self.logger.info(
                                        "Znaleziono uzyszkodnika ktory juz nie ma konta"
                                    )
                                    bad_id_cunter_counter += 1
                            # 4. return message success/failure - how many banned
                            await ctx.send(
                                "Obecnie na serwerze {guild} jest {bancunter} zbanowanych użytkowników. Znaleziono na tym kanale {tobancunter} id użytkowników do zbanowania. Zbanowano {cunter_counter} użytkowników. Na liście jest {bad_id_cunter_counter} użytkowników którzy skasowali już konto"
                            )
                        else:
                            await ctx.send(
                                f"Nie mam na serwerze {guild} uprawnień do banowania. :()"
                            )
                await ctx.send("Zrobione tak czy inaczej")
    @commands.hybrid_command(
        name="archive_channel",
        description="Jeśli nie wiesz jak użyć tej komendy to nawet nie próbuj",
        guild=discord.Object(id=664789470779932693),
    )
    @commands.has_any_role('Jarl', 'Thane')

    async def archive_channel(self, channel_no):
        """
        The function `archive_channel` retrieves messages from a specified channel, processes them, and
        saves the data in a JSON file with a timestamp in the filename.

        :param channel_no: The `archive_channel` function you provided seems to be incomplete. It looks like
        you are trying to archive messages from a Discord channel into a JSON file
        """
        channel = self.bot.get_channel(channel_no)
        messages = [message async for message in channel.history(limit=None)]
        path_newfile = f"{LOGSTORE}channeldump_{channel_no}_{datetime.now()}.json"
        new_data = []
        for message in messages:
            new_data.append(message)
        with open(path_newfile, "x", encoding=ENCODING) as new_file:
            new_file.seek(0)
            json.dump(new_data, new_file, indent=4)

    @tasks.loop(seconds=120)
    async def check_self(self):
        """
        The function `check_data` periodically checks for conditions to send messages and manage log files
        in a Discord channel.
        """
        # logger.info("Heartbeat of cleanup proc")
        channel = self.bot.get_channel(1062047367337095268)
        messages = [message async for message in channel.history(limit=1)]
        for mess in messages:
            channel = mess.channel
        if os.path.getsize(LOGFILE) > 60000000:
            await channel.send(
                "*Conjurer porządkuje bar, wypala szklanki do czysta miotaczem płomieni ze swojej zbroi i ogólnie wygląda na zajętego....*"
            )
            async with channel.typing():
                shutil.copyfile(LOGFILE, f"{LOGSTORE}discord{datetime.now()}")
                self.logger.info("Log rollover")
                handlers = self.logger.handlers
                for handler in handlers:
                    if isinstance(handler, logging.handlers.RotatingFileHandler):
                        handler.doRollover()
                await channel.send(
                    "*Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* ...sać powieści o naszym wspaniałym barze SZEFIE!"
                )

        if os.path.getsize(MEMORY_FIVE_MUZYKA) > 3000000:
            await channel.send(
                "*Conjurer porządkuje bar, wypala szklanki do czysta miotaczem płomieni ze swojej zbroi i ogólnie wygląda na zajętego....*"
            )
            async with channel.typing():
                path_newfile = f"{LOGSTORE}pamiec_muzyki{datetime.now()}.json"
                self.logger.info(path_newfile)
                with open(
                    MEMORY_FIVE_MUZYKA, "r+", encoding=ENCODING
                ) as file_music_memory:
                    with open(path_newfile, "x", encoding=ENCODING) as new_file:
                        # First we load existing data into a dict.
                        file_data = json.load(file_music_memory)
                        new_data = []
                        new_data.append(file_data[0])
                        new_data.extend(file_data[:-20])
                        file_music_memory.truncate(0)
                        file_music_memory.seek(0)
                        # convert back to json.
                        new_file.seek(0)
                        json.dump(new_data, file_music_memory, indent=4)
                        json.dump(file_data, new_file, indent=4)
                        await channel.send(
                            "*Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* ...sać powieści o naszym wspaniałym barze SZEFIE!"
                        )
        if os.path.getsize(MEMORY_FIVE_SIARA) > 3000000:
            path_newfile = f"{LOGSTORE}pamiec_rozmow{datetime.now()}.json"
            self.logger.info(path_newfile)
            await channel.send(
                "*Conjurer porządkuje bar, ścina lekkim laserem pulsacyjnym powierzchnie baru o grubości kilku mikronów i ogólnie wygląda na zajętego....*"
            )
            with open(MEMORY_FIVE_SIARA, "r+", encoding=ENCODING) as file:
                with open(path_newfile, "x", encoding=ENCODING) as new_file:
                    # First we load existing data into a dict.
                    file_data = json.load(file)
                    new_data = []
                    new_data.append(file_data[0])
                    new_data.extend(file_data[-20])
                    file.truncate(0)
                    file.seek(0)
                    new_file.seek(0)
                    # convert back to json.
                    json.dump(new_data, file, indent=4)
                    json.dump(file_data, new_file, indent=4)
                    await channel.send(
                        "*Przeciąga się za barem* No dobra - porobione to można dalej pi... *Zauważa spojrzenie Hammera* eprzyć o głupotach z klientami... Szefie... *Bierze 'ukradkowy' łyk z piersiówki*"
                    )
        global LAST_SPONTANEOUS_CALL
        global TIME_BETWEEN_CALLS
        tdelta = datetime.now() - LAST_SPONTANEOUS_CALL
        tdelta = tdelta.total_seconds()
        if tdelta > TIME_BETWEEN_CALLS:
            self.logger.info("Spontaneous call")
            # trunk-ignore(bandit/B311)
            TIME_BETWEEN_CALLS = random.randint(
                10200, 272800
            )  # temp random, set after each call
            self.logger.debug(TIME_BETWEEN_CALLS)
            LAST_SPONTANEOUS_CALL = datetime.now()
            async with channel.typing():
                message = await get_random_cyclic_message(self.bot)
                self.logger.info("Odpowiedz")
                self.logger.info(message)
                await channel.send(message)


async def setup(bot):
    logger = logging.getLogger("discord")
    am = AdministrationModule(bot, "discord")
    await bot.add_cog(am)
    am.check_self.start()
    logger.info("Loading administration commands module done")
