import json
import re
# needs the following version of discord.py for recurring events to work: https://github.com/DA-344/d.py/tree/recurrent_events
# uncomment following lines
# import sys 
# sys.path.insert(1, '/path/to/Posthorn/vendor/d.py')
import discord
from discord.ext import commands, tasks
from dotenv import load_dotenv
from os import getenv
from github_connector import create_or_update_file, delete_file, similar_exists


load_dotenv("./.env")
DISCORD_TOKEN = getenv("DISCORD_TOKEN")

# Intents are required to listen to events like messages
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
intents.reactions = True
intents.dm_messages = True

# Create an instance of the Bot
bot = commands.Bot(command_prefix="!", intents=intents)


def getCommitMessage(event):
    return f"{event.start_time.strftime('%Y-%m-%d')}_{event.name}"


def getFilePath(event):
    return f"_events/{event.start_time.strftime('%Y')}/discord-event-{event.id}.md"

def getDay(day):
    match day:
        case 0:
            return 'MO'
        case 1:
            return 'TU'
        case 2:
            return 'WE'
        case 3:
            return 'TH'
        case 4:
            return 'FR'
        case 5:
            return 'SA'
        case 6:
            return 'SU'
        case _:
            raise Exception(f'no matching day for int {day}')

def getRrule(event):
    byDay = False
    if hasattr(event.recurrence_rule, "n_weekdays"):
        weekdays = event.recurrence_rule.n_weekdays
        i = 0
        for week, day in weekdays:
            comma = ''
            if i < len(weekdays) - 1:
                comma = ','
            byDay = f'{str(week)}{getDay(day)}{comma}'
            i += 1
    if byDay:
        return f"FREQ={event.recurrence_rule.frequency.name.upper()};INTERVAL={event.recurrence_rule.interval};BYDAY={byDay}"
    else:
        return f"FREQ={event.recurrence_rule.frequency.name.upper()};INTERVAL={event.recurrence_rule.interval}"

def getEventYML(event):
    location = "LeibnizstraรŸe 32, 39104 Magdeburg"
    if hasattr(event, 'location'):
        location = event.location

    if hasattr(event, "recurrence_rule") and hasattr(event.recurrence_rule, "frequency") and hasattr(event.recurrence_rule, "interval"):
        return f'---\nlayout: event\ntitle: "{event.name}"\nauthor: "Netz39 e.V." \nevent:\n  start: {event.start_time.strftime("%Y-%m-%d %H:%M:%S")} \n  end:   {event.end_time.strftime("%Y-%m-%d %H:%M:%S")} \n  organizer: "Netz39 Team <kontakt@netz39.de>" \n  location: "{location}"\n  rrule: "{getRrule(event)}"\n---\n<!-- event imported from discord manual changes may be overwritten -->\n{event.description}'
    else:
        return f'---\nlayout: event\ntitle: "{event.name}"\nauthor: "Netz39 e.V." \nevent:\n  start: {event.start_time.strftime("%Y-%m-%d %H:%M:%S")} \n  end:   {event.end_time.strftime("%Y-%m-%d %H:%M:%S")} \n  organizer: "Netz39 Team <kontakt@netz39.de>" \n  location: "{location}"\n---\n<!-- event imported from discord manual changes may be overwritten -->\n{event.description}'


def getAdmins(guild):
    admins = []
    for member in guild.members:
        if member.guild_permissions.administrator:
            admins.append(member.id)
    return admins


@tasks.loop(hours=24)  # Runs every 7 days
async def check_events():
    await bot.wait_until_ready()
    for guild in bot.guilds:
        scheduledEvents = await guild.fetch_scheduled_events()
        for event in scheduledEvents:
            await handleEvent(event, True)


async def handleEvent(event, isUpdate = False):
    guild = event.guild
    channel = guild.system_channel
    admins = getAdmins(guild)

    eventYML = getEventYML(event)
    file_path = getFilePath(event)
    commit_message = getCommitMessage(event)
    if event.creator_id in admins or similar_exists(event.name):
        create_or_update_file(
            file_path,
            eventYML,
            f"new event: {commit_message}",
        )
        if not isUpdate:
            embed = discord.Embed(
                title=f"๐Ÿ“… New event on {event.start_time.strftime('%Y-%m-%d')}",
                description=f"### {event.name}\n\n{event.description}\n\n",
                color=discord.Color.light_embed()
            )
            if hasattr(event, "cover_image") and event.cover_image:
                embed.set_image(url=event.cover_image.url)
            embed.add_field(name="๐Ÿ“… Time", value=event.start_time.strftime("%Y-%m-%d %HUhr"), inline=True)
            if hasattr(event, 'location'):
                embed.add_field(name="๐Ÿ“ Location", value=event.location, inline=True)
            eventLink = f"[๐Ÿ”— Netz39](https://www.netz39.de/events/2025/discord-event-{event.id})";
            embed.add_field(value=eventLink, name="", inline=False)
            embed.set_footer(text="Donโ€™t miss it! ")
            await channel.send(embed=embed)
    else:
        eventJSON = json.dumps(
            {
                "yml": eventYML,
                "path": file_path,
                "fileName": commit_message,
                "name": event.name,
                "date": event.start_time.strftime("%Y-%m-%d"),
            },
            indent=2,
        )
        for admin in admins:
            user = await bot.fetch_user(event.creator_id)
            await sendDm(
                admin,
                f"{user.name} added a new Event on {event.start_time.strftime('%Y')}: {event.name}. Like this message to approve.\n\n\n```json\n{eventJSON}```",
            )


# Event: When the bot is ready
@bot.event
async def on_ready():
    print(f"Logged in as {discord.user}")


# Command: !hello
@bot.command()
async def hello(ctx):
    await ctx.send(
        "Hello, I am a bot to add events from discord to the Netz39 calendar on the website!"
    )


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")
    for guild in bot.guilds:
        system_channel = guild.system_channel
        if system_channel:
            await system_channel.send("Hello everyone! I'm online and ready to go! ๐Ÿ“…")
    await check_events()


@bot.event
async def on_scheduled_event_update(before, after):
    if after.status != "canceled":
        await handleEvent(after, True)


@bot.event
async def on_scheduled_event_create(event):
    await handleEvent(event)


async def sendDm(userID, message):
    user = await bot.fetch_user(userID)
    await user.send(message)


@bot.event
async def on_reaction_add(reaction, user):
    message = reaction.message
    channel = message.channel
    guild = message.guild

    admins = getAdmins(guild)
    if not user.id in admins:
        return  # Ignore non-maintainers
    if user.bot:
        return  # Ignore bot reactions

    if (
        isinstance(message.channel, discord.DMChannel) and message.author.bot
    ):  # Check if it's a DM and from the bot
        if reaction.emoji == "๐Ÿ‘":
            messageJSON = json.loads(
                re.search(
                    "(?s)(?<=```json).*?(?=```)", reaction.message.content
                ).group()
            )
            create_or_update_file(
                messageJSON["path"],
                messageJSON["yml"],
                f"new event: {messageJSON['fileName']}",
            )
            await channel.send(
                f"๐Ÿ“… New event '{messageJSON['name']}' on {messageJSON['date']}."
            )


@bot.event
async def on_scheduled_event_delete(event):
    delete_file(getFilePath(event))
    guild = event.guild
    channel = guild.system_channel
    await channel.send(f"โŒ Event has been canceled: {event.name}")


bot.run(DISCORD_TOKEN)