import os
import asyncio
import dalle3
import discord
import responses
from invoke import Executor
from dotenv import load_dotenv
from discord.ext import commands

class Bot:
    def __init__(self, agent, llm, command_prefix="!"):
        load_dotenv()
        
        intents = discord.intents.default()
        intents.messages = True
        intents.guilds = True
        intents.voice_states = True
        intents.message_content = True

        # setup
        self.llm = llm
        self.agent = agent
        self. bot = commands.bot(command_prefix="!", intents=intents)
        self.discord_token = os.getenv("DISCORD_TOKEN")
        self.storage_service = os.getenv("STORAGE_SERVICE")


        @self.bot.event
        async def on_ready():
            print(f"we have logged in as {self.bot.user}")


        @self.bot.command()
        async def greet(ctx):
            """greets the user."""
            await ctx.send(f"hello, {ctx.author.name}!")


        @self.bot.command()
        async def help_me(ctx):
            """provides a list of commands and their descriptions."""
            help_text = """
            - `!greet`: greets you.
            - `!run [description]`: generates a video based on the given description.
            - `!help_me`: provides this list of commands and their descriptions.
            """
            await ctx.send(help_text)

        @self.bot.event
        async def on_command_error(ctx, error):
            """handles errors that occur while executing commands."""
            if isinstance(error, commands.commandnotfound):
                await ctx.send("that command does not exist!")
            else:
                await ctx.send(f"an error occurred: {error}")

        @self.bot.command()
        async def join(ctx):
            """joins the voice channel that the user is in."""
            if ctx.author.voice:
                channel = ctx.author.voice.channel
                await channel.connect()
            else:
                await ctx.send("you are not in a voice channel!")

        @self.bot.command()
        async def leave(ctx):
            """leaves the voice channel that the self.bot is in."""
            if ctx.voice_client:
                await ctx.voice_client.disconnect()
            else:
                await ctx.send("i am not in a voice channel!")

        # voice_transcription.py
        @self.bot.command()
        async def listen(ctx):
            """starts listening to voice in the voice channel that the bot is in."""
            if ctx.voice_client:
                # create a wavesink to record the audio
                sink = discord.sinks.wavesink('audio.wav')
                # start recording
                ctx.voice_client.start_recording(sink)
                await ctx.send("started listening and recording.")
            else:
                await ctx.send("i am not in a voice channel!")
            
        # image_generator.py
        @self.bot.command()
        async def generate_image(ctx, *, prompt: str):
            """generates images based on the provided prompt"""
            await ctx.send(f"generating images for prompt: `{prompt}`...")
            loop = asyncio.get_event_loop()

            # initialize a future object for the dalle instance
            model_instance = dalle3()
            future = loop.run_in_executor(Executor, model_instance.run, prompt)

            try:
                # wait for the dalle request to complete, with a timeout of 60 seconds
                await asyncio.wait_for(future, timeout=300)
                print("done generating images!")

                # list all files in the save_directory
                all_files = [os.path.join(root, file) for root, _, files in os.walk(os.environ("SAVE_DIRECTORY")) for file in files]

                # sort files by their creation time (latest first)
                sorted_files = sorted(all_files, key=os.path.getctime, reverse=True)

                # get the 4 most recent files
                latest_files = sorted_files[:4]
                print(f"sending {len(latest_files)} images to discord...")

                # send all the latest images in a single message
                storage_service = os.environ("STORAGE_SERVICE") # "https://storage.googleapis.com/your-bucket-name/
                await ctx.send(files=[storage_service.upload(filepath) for filepath in latest_files])

            except asyncio.timeouterror:
                await ctx.send("the request took too long! it might have been censored or you're out of boosts. please try entering the prompt again.")
            except Exception as e:
                await ctx.send(f"an error occurred: {e}")

        @self.bot.command()
        async def send_text(ctx, *, text: str, use_agent: bool = True):
            """sends the provided text to the worker and returns the response"""
            if use_agent:
                response = self.agent.run(text)
            else:
                response = self.llm.run(text)
            await ctx.send(response)
        
        def add_command(self, name, func):
            @self.bot.command()
            async def command(ctx, *args):
                reponse = func(*args)
                await ctx.send(responses)
                
def run(self) :
    self.bot.run("DISCORD_TOKEN")