import discord
import threading
import os
import gradio as gr
from discord.ext import commands
from slack_sdk import WebClient
from slack_sdk.errors import SlackApiError
import aiojobs
import asyncio
from datetime import datetime, timedelta
from apscheduler.executors.pool import ThreadPoolExecutor
from apscheduler.schedulers.background import BackgroundScheduler


DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
SLACK_BOT_TOKEN = os.getenv('BOT_USER_OAUTH_TOKEN_HF')

# real = os.getenv('SLACK_CHANNEL_ID_HF')
# test = 'C07B4KNU5BQ'
SLACK_CHANNEL_ID = os.getenv('SLACK_CHANNEL_ID_HF')
SLACK_CHANNEL_ID_TEST = 'C07B4KNU5BQ'
# 1259415803879751700    = test forum
# 1019883044724822016    = ask for help
ASK_FOR_HELP_CHANNEL_ID = 1019883044724822016
GRADIO_CHANNEL_ID = 1025174734427656283
ARGILLA_HELP_CHANNEL_ID = 1253640751481356330
DATA_DISCUSSIONS_CHANNEL_ID = 1217179426002047076


TRIGGERS = {
    ("discord bot",): "<@U051DB2754M>", # adam
    ("autotrain",): "<@U01E3LEC2N7>", # abhishek
    ("auto train",): "<@U01E3LEC2N7>", # abhishek
    ("competition",): "<@U01E3LEC2N7>", # abhishek
    ("competitions",): "<@U01E3LEC2N7>", # abhishek
    ("text to speech",): "<@U039C2GANMV>", # VB
    ("tts",): "<@U039C2GANMV>", # VB
    ("asr",): "<@U039C2GANMV>", # VB
    ("musicgen",): "<@U039C2GANMV>", # VB
    ("whisper",): "<@U039C2GANMV>", # VB
    ("speech recognition",): "<@U039C2GANMV>", # VB
    ("bark",): "<@U039C2GANMV>", # VB
    ("sentence-transformers",): "<@U04E4DNPWG7>", # tom aarsen
    ("sentence_transformers",): "<@U04E4DNPWG7>", # tom aarsen
    ("setfit",): "<@U04E4DNPWG7>", # tom aarsen
    ("sentence transformers",): "<@U04E4DNPWG7>", # tom aarsen
    ("argilla",): "<@U076B8C7G3E>", # david berenstein
    ("distilabel",): "<@U076B8C7G3E>", # david berenstein
    ("argilla",): "<@U0766H30T7F>", # natalia elvira
    ("dataset",): "<@U0766H30T7F>", # natalia elvira
    ("docs",): "<@U02DATT4C5B>", # steven liu
    ("documentation",): "<@U02DATT4C5B>", # steven liu
    ("gradio",): "<@U02NMK75F1V>", # abubakar abid
    ("gradio",): "<@U04FLGQ26PQ>", # yuvraj sharma
    ("argilla",): "<@U076MF65WEM>", # sara han diaz lorenzo
    ("distilabel",): "<@U076MF65WEM>", # sara han diaz lorenzo
    ("argilla",): "<@U0765RENPNZ>", # sara han diaz lorenzo
    ("distilabel",): "<@U0765RENPNZ>", # sara han diaz lorenzo
    ("dataset", "feedback"): "<@U0768RCHCRY>", # ben burtenshaw
    ("distilabel",): "<@U0768QEN0LA>", # Gabriel Martín Blázquez
    ("distilabel",): "<@U076271MBUN>", # Agustín Piqueres
    
}

daily_pings = []

intents = discord.Intents.all()
intents.messages = True
bot = commands.Bot(command_prefix='!', intents=intents)

slack_client = WebClient(token=SLACK_BOT_TOKEN)

thread_mapping = {}



@bot.event
async def on_ready():
    print(f'Logged in as {bot.user}')

@bot.event
async def on_message(message):
    if message.author == bot.user:
        return

    # notification bot
    print("on_message")
    huggingfolks_role = discord.utils.get(message.guild.roles, id=897376942817419265)
    bots_role = discord.utils.get(message.guild.roles, id=1258328471609016341)
    if huggingfolks_role not in message.author.roles: # no need for ping if we're already discussing
        if bots_role not in message.author.roles: # bots shouldn't trigger pings for this
            print(" not bot ")
            content = message.content.lower()
            
            for trigger, slack_mention in TRIGGERS.items():
                if all(word in content for word in trigger):         
                    daily_pings.append({
                        'author': str(message.author),
                        'content': message.content,
                        'channel': message.channel.name,
                        'url': message.jump_url,
                        'mention': slack_mention,
                        'trigger': trigger
                    })
                    print(f"daily pings:{daily_pings}")
                    break

    # Check if the message is in a thread
    if isinstance(message.channel, discord.Thread):
        discord_thread_id = message.channel.id
        # Check if there's an existing Slack thread for this Discord thread
        # (the only Slack threads created should be for forum channel threads, not just any thread)
        if discord_thread_id in thread_mapping:
            slack_thread_ts = thread_mapping[discord_thread_id]
            # post to slack only if thread already exists
            post_to_slack_forum_version(message, SLACK_CHANNEL_ID, message.content, message.author, thread_ts=slack_thread_ts)

    await bot.process_commands(message)


@bot.event
async def on_thread_create(thread):
    # (discord) must be the child thread of the CORRECT forum channel(s) (not just any thread, or any forum channel)
    if isinstance(thread.parent, discord.ForumChannel) and thread.parent.id in {ASK_FOR_HELP_CHANNEL_ID, GRADIO_CHANNEL_ID, ARGILLA_HELP_CHANNEL_ID, DATA_DISCUSSIONS_CHANNEL_ID}:
        discord_thread_id = thread.id
        slack_thread_ts = post_to_slack_create_thread(
            SLACK_CHANNEL_ID,
            f"New forum thread started in {thread.parent.name} by {thread.owner}: *{thread.name}*\n"
            f"{thread.jump_url}"
        )
        if slack_thread_ts:
            thread_mapping[discord_thread_id] = slack_thread_ts


def post_to_slack_forum_version(message, channel, text, author, thread_ts=None):
    if message.attachments:
        for attachment in message.attachments:
            attachment_url = attachment.url
            text += f"\nAttachment: {attachment_url}"
    text = f"{author}" + ": " + text
    try:
        response = slack_client.chat_postMessage(
            channel=channel,
            text=text,
            thread_ts=thread_ts
        )
        return response['ts']  # Return the Slack message timestamp (thread ID)
    except SlackApiError as e:
        print(f"Error posting to Slack: {e.response['error']}")
        return None


def post_to_slack_create_thread(channel, text, thread_ts=None):
    try:
        response = slack_client.chat_postMessage(
            channel=channel,
            text=text,
            thread_ts=thread_ts
        )
        return response['ts']  # Return the Slack message timestamp (thread ID)
    except SlackApiError as e:
        print(f"Error posting to Slack: {e.response['error']}")
        return None      


@bot.command()
async def list_tags(ctx, forum_channel_id: int):
    if ctx.author.id == 811235357663297546:
        forum_channel = bot.get_channel(forum_channel_id)
        if isinstance(forum_channel, discord.ForumChannel):
            tags = forum_channel.available_tags
            tag_list = [tag.name for tag in tags]
            await ctx.send(f'Available tags: {", ".join(tag_list)}')

#----------------------------------------------------------------------------------------------

def send_daily_pings():
    global daily_pings
    if daily_pings:
        print("sending daily pings...")
        # combine into one message
        combined_message = '\n'.join(f"{ping['mention']} (for the keyword -> '{ping['trigger']}')\nFrom {ping['author']} in channel #{ping['channel']}: {ping['content']}\n{ping['url']}" for ping in daily_pings)
        #await post_to_slack(None, combined_message, SLACK_CHANNEL_ID_TEST, None, None, None)

        
        response = slack_client.chat_postMessage(
            channel=SLACK_CHANNEL_ID,
            text=combined_message,
            unfurl_links=False,
            unfurl_media=False            
        )        

        
        daily_pings = []  # reset after posting

# pings -------------------------------------------------------------------------------------------

async def post_to_slack(author, content, channel, url, slack_mention, trigger):
    try:
        response = slack_client.chat_postMessage(
            channel=SLACK_CHANNEL_ID,
            text=f"{slack_mention} (for the keyword -> '{trigger}')\nFrom {author} in channel #{channel}: {content}\n{url}"
        )
    except SlackApiError as e:
        print(f"Error posting to Slack: {e.response['error']}")


executor = ThreadPoolExecutor(max_workers=1)
scheduler = BackgroundScheduler(executors={'default': executor})
scheduler.add_job(send_daily_pings, trigger='interval', days=1)
scheduler.start()


# runs discord bot in thread = helps avoid blocking calls
def run_bot():
    bot.run(DISCORD_TOKEN)
threading.Thread(target=run_bot).start()
def greet(name):
    return "Hello " + name + "!"
demo = gr.Interface(fn=greet, inputs="text", outputs="text")
demo.launch()