import openai
# from openai import api_key
import discord
from discord import Intents
from discord.commands import slash_command, option
from discord.ext import commands
import re
import os
import asyncio
import logging
import datetime
import base64
import requests
import zipfile
from zipfile import ZipFile
from dotenv import load_dotenv
load_dotenv()
use_images = os.getenv("USE_IMAGES")
cooldown = os.getenv("COOLDOWN")
if use_images != "No": import imagesGeneration
logging.basicConfig(level=logging.INFO)
imageint = ""
if use_images != "No": imageint = "To add an image illustration , use ![bg left:50% 70%](a-long-detailed-description-of-the-image.png) at the beginning of the slide, just after \"---\". Use only .png. It's not possible to add technical images but only illustrations. The images are generated by an ai, the name of the file should be a detailed quite long description of the image wanted.  For example \" ![bg left:50% 100%](a-man-wearing-a hat-ryding-a-bicicle.png)\" but don't need to show a person necessairly."
intstructions = f'''Here is a presentation with marp. It's not possible to make slides longer than 200 characters. to separate slides, 
"

---

"
 then go at the line. The presentatio should be for everybody, all technical words and concepts, explained. {imageint} The presentation is minimum 20 slides long. You can use bulletpoints. Use markdown formatting (titles, etc...). The presentation has also a conclusion.'''
bot = discord.Bot()

styles = ["default", "gaia", "uncover", "default-dark", "gaia-dark", "uncover-dark", "olive"]
languages = ["english", "french", "spanish", "german", "italian", "portuguese", "russian", "chinese", "japanese", "korean", "arabic"]
darkstyles = ["default-dark", "gaia-dark", "uncover-dark"]
customstyles = ["olive"]
async def get_style(ctx: discord.AutocompleteContext):
    """Returns a list of colors that begin with the characters entered so far."""
    return [style for style in styles if style.startswith(ctx.value.lower())]
async def get_ln(ctx: discord.AutocompleteContext):
    return [language for language in languages if language.startswith(ctx.value.lower())]

@bot.slash_command(name="private_present", description="Generate a presentation with marp, private command for user 707196665668436019")
@option(name="subject", description="The subject of the presentation", required=True)
@option(name="style", description="The style of the presentation", required=False, autocomplete=get_style)
@option(name="center", description="Center the text", required=False)
@option(name="language", description="The language of the presentation", required=False, autocomplete=get_ln)
@option(name="indications", description="The indications for the presentation", required=False)
#command wprks only in dm and only for user 707196665668436019
@commands.is_owner()
async def private_present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
    await present(ctx, subject, style, language, indications)




@bot.slash_command(name="present", description="Generate a presentation with marp")
#we create a function that takes the subject of the presentation and the style of the presentation as arguments, and that
@option(name="subject", description="The subject of the presentation", required=True)
@option(name="style", description="The style of the presentation", required=False, autocomplete=get_style)
@option(name="center", description="Center the text", required=False)
@option(name="language", description="The language of the presentation", required=False, autocomplete=get_ln)
@option(name="indications", description="The indications for the presentation", required=False)
# a cooldown of duration cooldown seconds, except if the user is 707196665668436019

#@commands.cooldown(1, int(cooldown), commands.BucketType.user)
@commands.cooldown(1, int(cooldown), commands.BucketType.guild)
async def normal_present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
    await present(ctx, subject, style, language, indications)


async def present(ctx: discord.ApplicationContext, subject: str, style: str = "default", center: bool = True, language: str = "english", indications: str = ""):
    await ctx.defer()
    date = datetime.datetime.now()
    date = date.strftime("%Y-%m-%d-%H-%M-%S")
    #if the style is dark
    dark = False
    if style in darkstyles: 
        style = style.replace("-dark", "")
        dark = True 
    marp = f'''---
marp: true
theme: {styles[styles.index(style)]}
class:
'''
    if dark: marp = marp + f"    - invert\n---"
    if center: marp = marp + "    - lead\n---"
    else: marp = marp + "\n---"
#    if style in customstyles: 
#        marp = f"/* @theme {style} */\n" + marp
#        print(marp)
    prompt = f"{intstructions} {indications} The subject of the presentation is: {subject} The Language is: {language} <|endofprompt|> \n {marp}"    
    subject2 = subject
    forbidden = ["\\", "/", "?", "!", ":", ";", "(", ")", "[", "]", "{", "}", "'", '"', "=", "+", "*", "&", "^", "%", "$", "#", "@", "`", "~", "|", "<", ">", ",", ".", "?", " "]
    for i in forbidden: 
        if i in subject: subject = subject.replace(i, "-")
    #we save teh subject in base64 in a variable
    #if dosen't exist, create a directory called "userid" where the userid is the id of the user who called the command
    uid = str(ctx.author.id)
    if not os.path.exists("./data/"+uid):
        os.mkdir("./data/"+uid)
    datenow = datetime.datetime.now()
    datenow = datenow.strftime("%Y-%m-%d-%H-%M-%S")
    response = await openai.Completion.acreate(
        engine="text-davinci-003",
        prompt=prompt,
        temperature=0.6,
        max_tokens=1024,
        top_p=1,
        frequency_penalty=0,
        presence_penalty=0,
        stop=["<|endofprompt|>"]
    )
    #we save the output in a variable
    output = response["choices"][0]["text"]
    present = marp + output
    ##we save the output in a file called "subject.md"
    matches = re.finditer(r'!\[.*?\]\((.*?)\)', present)
    image_filenames = []
    for match in matches:
        image_filenames.append(match.group(1))
    #we create a text file with the image names and a md file for the presentation with utf8 encoding
    if len(subject) > 15: subject = subject[:15]    
    b64 = base64.urlsafe_b64encode(subject.encode("utf-8"))
    os.mkdir(f"./data/{uid}/{b64}{datenow}")
    path = f"./data/{uid}/{b64}{datenow}"
    with open(f"{path}/{subject}-images.txt", "w", encoding="utf8") as f:
        for image in image_filenames:
            f.write(image + "\n")
    with open(f"{path}/{subject}.md", "w", encoding="utf8") as f: f.write(present)
    if len(image_filenames) > 0 and  use_images!="no":
        #now we first remove the extension from the image filenames by removing the last 4 characters
        image_filenames = [image[:-4] for image in image_filenames]
        print(image_filenames)
        for images in image_filenames:
            print ("generating image " + images + "with " + str(use_images))
            r = await imagesGeneration.generate(images, f"{os.getcwd()}\\data\\{uid}\\{b64}{datenow}\\", str(use_images), apikey)
            if str(use_images) == "sd": os.rename(f"{os.getcwd()}\\.\\data\\{uid}\\{b64}{datenow}\\{images}_0.png", f"{os.getcwd()}\\data\\{uid}\\{b64}{datenow}\\{images}.png")
            if str(use_images) == "dalle":
                image_url = r['data'][0]['url']
                img_data = requests.get(image_url).content
                with open(f'{path}/{images}.png', 'wb') as handler:
                    handler.write(img_data)
                await asyncio.sleep(15) #wait 15 seconds to avoid rate limiting
    cmd = f"--pdf --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css"
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'")
        os.system(f"./marp {cmd}")
    cmd = f" --image png -o {path}/{subject}.png --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css"
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'")
        os.system(f"./marp {cmd}")
    cmd = f" --html --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css" 
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'") 
        os.system(f"./marp {cmd}")
    cmd = f" --pptx --allow-local-files {path}/{subject}.md"
    if style in customstyles: cmd = cmd + f" --theme ./themes/{style}.css" 
    if os.path.exists("./marp.exe"):
        os.system(f"marp.exe {cmd}")
    else:
        cmd = cmd.replace("'", "\\'") 
        os.system(f"./marp {cmd}")
    #now, we create a zip file with all the files
    zipObj = ZipFile(f"{path}/{subject}.zip", 'w')
    zipObj.write(f"{path}/{subject}.md", f"{subject}.md")
    zipObj.write(f"{path}/{subject}.html", f"{subject}.html")
    zipObj.write(f"{path}/{subject}.pdf", f"{subject}.pdf")
    zipObj.write(f"{path}/{subject}.png", f"{subject}.png")
    zipObj.write(f"{path}/{subject}.pptx", f"{subject}.pptx")
    with open(f"{path}/{subject}-images.txt", "r", encoding="utf8") as f:
        for image in f.readlines():
            zipObj.write(f"{path}/{image.strip()}", f"{image.strip()}")
    zipObj.close()
    embed = discord.Embed(title=subject2, description="Thanks for using presentator bot. You will find your presentation in the attached zip file in the following formats: markdown, html, pdf, pptx, and the presentation' images. If you want to modify your presentation you can use the markdown file. More information about how to modify the file [HERE](https://marp.app).", color=discord.Color.brand_red())
    files = [discord.File(f"{path}/{subject}.zip"), discord.File(f"{path}/{subject}.png")]
    embed.set_image(url=f"attachment://{subject}.png")
    await ctx.respond(embed=embed, files=files)

@bot.slash_command(name="list", description="List all the presentations you have created")
async def list(ctx: discord.ApplicationContext):
    embed = discord.Embed(title="Presentations", description="Here is the list of all the presentations you have created. You can download the presentation in different formats (pdf, markdown, html) by doing `/get` \"*presentation id*\". The images are generated by an ai. If you want to modify your presentation you can use the markdown file. More information about how to modify the file [HERE](https://marp.app).", color=0x00ff00)
    liste = await get_presentations(str(ctx.author.id))
    for key in liste:
        embed.add_field(name=f"{liste[key]}", value=f"</get:1063051827010084925> `{key}`", inline=False)
    await ctx.respond(embed=embed, ephemeral=True)

async def get_presentations(uid):
    folders = os.listdir(f"./data/{uid}")
    names = {}
    for folder in folders:
        name = base64.urlsafe_b64decode(folder[2:-20]).decode("utf-8")
        names[folder] = name
    return names

@bot.slash_command(name="get", description="Get a presentation")
@option(name="pid", description="The id of the presentation", required=True)
async def get(ctx: discord.ApplicationContext, pid: str):
    uid = str(ctx.author.id)
    liste = await get_presentations(uid)
    if pid in liste:
        files = [discord.File(f"./data/{uid}/{pid}/{liste[pid]}.pdf"), discord.File(f"./data/{uid}/{pid}/{liste[pid]}.md"), discord.File(f"./data/{uid}/{pid}/{liste[pid]}.html")]
        await ctx.respond(files=files, ephemeral=True)
#when the bot is ready we print a message
@bot.event
async def on_ready():
    print("Bot is ready")
    if not os.path.exists("data"):
        os.mkdir("data")
@bot.event
async def on_application_command_error(ctx, error):
    #if there is an error we send a message to the user
    await ctx.respond(f"An error occured: {error}", ephemeral=True)

#get the openai key drom he key.env file
token = os.getenv("TOKEN")
apikey = os.getenv("OPENAI")
openai.api_key = apikey
bot.run(token)