import os
import io
import asyncio
from datetime import datetime, timedelta

import nextcord
from nextcord.ext import commands, tasks
from nextcord import Interaction, SlashOption, Permissions
from nextcord.ui import Modal, TextInput, Button, View, Select
from nextcord.utils import get
from dotenv import load_dotenv
import motor.motor_asyncio
import aiohttp
import urllib.parse

load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
MONGO_URI = os.getenv('MONGO_URI')
ROLIMONS_API_KEY = os.getenv('ROLIMONS_API_KEY')

mongo_client = motor.motor_asyncio.AsyncIOMotorClient(MONGO_URI)
db = mongo_client['discord_bot']

intents = nextcord.Intents.all()
intents.members = True
intents.message_content = True

bot = commands.Bot(command_prefix='!', intents=intents)

YOUR_GUILD_ID = int(os.getenv('YOUR_GUILD_ID'))
STAFF_CHANNEL_ID = int(os.getenv('STAFF_CHANNEL_ID'))
SCAM_ALERTS_CHANNEL_ID = int(os.getenv('SCAM_ALERTS_CHANNEL_ID'))
BLACKLIST_ROLE_ID = int(os.getenv('BLACKLIST_ROLE_ID'))
SCAMMER_ROLE_ID = int(os.getenv('SCAMMER_ROLE_ID'))
STOCKER_ROLE_IDS = [int(role_id) for role_id in os.getenv('STOCKER_ROLE_IDS', '').split(',') if role_id.isdigit()]
ROLE_ID_FOR_1M = int(os.getenv('ROLE_ID_FOR_1M'))
ROLE_ID_FOR_100K = int(os.getenv('ROLE_ID_FOR_100K'))
ROLE_ID_FOR_25K = int(os.getenv('ROLE_ID_FOR_25K'))

class ApplicationReviewView(View):
    def __init__(self, application_id, user_id, role_type):
        super().__init__(timeout=None)
        self.application_id = application_id
        self.user_id = user_id
        self.role_type = role_type

    @nextcord.ui.button(label="Approve", style=nextcord.ButtonStyle.green, custom_id="approve_button")
    async def approve_button(self, button: Button, interaction: Interaction):
        if not interaction.user.guild_permissions.manage_roles:
            await interaction.response.send_message("You do not have permission to perform this action.", ephemeral=True)
            return

        # Assign role to user
        guild = bot.get_guild(YOUR_GUILD_ID)
        member = guild.get_member(self.user_id)
        if member:
            role = get(guild.roles, name=self.role_type.replace("_", " ").title())
            if role:
                await member.add_roles(role)
                # Send DM to user
                try:
                    await member.send(f"Congratulations! Your application for the **{role.name}** role has been approved.")
                except:
                    pass
            else:
                await interaction.response.send_message("Role not found.", ephemeral=True)
                return
        else:
            await interaction.response.send_message("Member not found.", ephemeral=True)
            return

        # Update application status in database
        await db.applications.update_one(
            {'_id': self.application_id},
            {'$set': {'status': 'approved', 'approved_by': interaction.user.id, 'approved_at': datetime.utcnow()}}
        )

        # Acknowledge the interaction and disable buttons
        await interaction.response.send_message("Application approved.", ephemeral=True)
        self.disable_all_items()
        await interaction.message.edit(view=self)

    @nextcord.ui.button(label="Deny", style=nextcord.ButtonStyle.red, custom_id="deny_button")
    async def deny_button(self, button: Button, interaction: Interaction):
        if not interaction.user.guild_permissions.manage_roles:
            await interaction.response.send_message("You do not have permission to perform this action.", ephemeral=True)
            return

        # Send DM to user
        guild = bot.get_guild(YOUR_GUILD_ID)
        member = guild.get_member(self.user_id)
        if member:
            try:
                await member.send(f"Sorry, your application for the **{self.role_type.replace('_', ' ').title()}** role has been denied.")
            except:
                pass
        else:
            await interaction.response.send_message("Member not found.", ephemeral=True)
            return

        # Update application status in database
        await db.applications.update_one(
            {'_id': self.application_id},
            {'$set': {'status': 'denied', 'denied_by': interaction.user.id, 'denied_at': datetime.utcnow()}}
        )

        # Acknowledge the interaction and disable buttons
        await interaction.response.send_message("Application denied.", ephemeral=True)
        self.disable_all_items()
        await interaction.message.edit(view=self)

async def get_total_value(roblox_id):
    total_value = 0
    async with aiohttp.ClientSession() as session:
        async with session.get(f'https://www.rolimons.com/playerapi/player/{roblox_id}') as resp:
            if resp.status != 200:
                return None
            data = await resp.json()
            if not data.get('success', False):
                return None
            inventory_items = data.get('player', {}).get('item_values', {})
            async with session.get('https://www.rolimons.com/itemapi/itemdetails') as items_resp:
                if items_resp.status != 200:
                    return None
                items_data = await items_resp.json()
                item_details = items_data.get('items', {})
            for item_id_str, item_count in inventory_items.items():
                item_id = int(item_id_str)
                item_info = item_details.get(item_id_str)
                if item_info:
                    item_value = item_info[3]
                    total_value += item_value * item_count
    return total_value

async def download_attachment(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as resp:
            if resp.status == 200:
                data = await resp.read()
                return nextcord.File(io.BytesIO(data), filename=url.split('/')[-1])
    return None

@bot.event
async def on_ready():
    check_blacklists.start()
    check_purges.start()
    backup_database.start()
    bot.add_view(ApplicationReviewView(None, None, None))  # Register the view instance
    print(f'Logged in as {bot.user} (ID: {bot.user.id})')

@bot.event
async def on_member_update(before, after):
    if before.roles != after.roles:
        await track_roles(after)

@bot.event
async def on_member_join(member):
    await restore_member_roles(member)

@bot.slash_command(name='snapshot', description='Take a snapshot of the server structure and configuration.')
@commands.has_permissions(administrator=True)
async def snapshot(interaction: Interaction):
    await interaction.response.defer(ephemeral=True)
    guild = interaction.guild
    roles = []
    for role in guild.roles:
        roles.append({
            'id': role.id,
            'name': role.name,
            'permissions': role.permissions.value,
            'color': role.color.value,
            'hoist': role.hoist,
            'position': role.position,
            'mentionable': role.mentionable,
            'managed': role.managed
        })
    channels = []
    for channel in guild.channels:
        overwrites = []
        for target, overwrite in channel.overwrites.items():
            target_id = target.id if isinstance(target, (nextcord.Role, nextcord.Member)) else target
            overwrites.append({
                'id': target_id,
                'type': 'role' if isinstance(target, nextcord.Role) else 'member',
                'allow': overwrite.pair()[0].value,
                'deny': overwrite.pair()[1].value
            })
        channels.append({
            'id': channel.id,
            'name': channel.name,
            'type': str(channel.type),
            'position': channel.position,
            'category_id': channel.category_id,
            'overwrites': overwrites
        })
    messages = []
    important_channels = ['announcements', 'rules']
    for channel_name in important_channels:
        channel = get(guild.channels, name=channel_name)
        if channel and isinstance(channel, (nextcord.TextChannel, nextcord.ForumChannel)):
            async for message in channel.history(limit=100):
                messages.append({
                    'channel_id': channel.id,
                    'content': message.content,
                    'embeds': [embed.to_dict() for embed in message.embeds],
                    'attachments': [attachment.url for attachment in message.attachments],
                    'author_id': message.author.id,
                    'timestamp': message.created_at.isoformat()
                })
    snapshot_data = {
        'guild_id': guild.id,
        'timestamp': datetime.utcnow(),
        'roles': roles,
        'channels': channels,
        'messages': messages
    }
    await db.snapshots.insert_one(snapshot_data)
    await interaction.followup.send('Server snapshot has been taken.', ephemeral=True)

@bot.slash_command(name='restore', description='Restore the server from the latest snapshot.')
@commands.has_permissions(administrator=True)
async def restore(interaction: Interaction):
    await interaction.response.defer(ephemeral=True)
    guild = interaction.guild
    snapshot = await db.snapshots.find_one({'guild_id': guild.id}, sort=[('timestamp', -1)])
    if not snapshot:
        await interaction.followup.send('No snapshot available to restore.', ephemeral=True)
        return
    existing_roles = {role.name: role for role in guild.roles}
    role_mapping = {}
    for role_data in snapshot['roles']:
        if role_data['name'] in existing_roles:
            role = existing_roles[role_data['name']]
            await role.edit(
                permissions=nextcord.Permissions(role_data['permissions']),
                color=nextcord.Color(role_data['color']),
                hoist=role_data['hoist'],
                mentionable=role_data['mentionable']
            )
        else:
            role = await guild.create_role(
                name=role_data['name'],
                permissions=nextcord.Permissions(role_data['permissions']),
                color=nextcord.Color(role_data['color']),
                hoist=role_data['hoist'],
                mentionable=role_data['mentionable']
            )
        role_mapping[role_data['id']] = role.id
    existing_channels = {channel.name: channel for channel in guild.channels}
    channel_mapping = {}
    for channel_data in snapshot['channels']:
        category = None
        if channel_data['category_id']:
            category_id = role_mapping.get(channel_data['category_id'], channel_data['category_id'])
            category = guild.get_channel(category_id)
        overwrites = {}
        for overwrite_data in channel_data['overwrites']:
            target = None
            if overwrite_data['type'] == 'role':
                target = guild.get_role(role_mapping.get(overwrite_data['id'], overwrite_data['id']))
            else:
                target = guild.get_member(overwrite_data['id'])
            if target:
                overwrite = nextcord.PermissionOverwrite.from_pair(
                    nextcord.Permissions(overwrite_data['allow']),
                    nextcord.Permissions(overwrite_data['deny'])
                )
                overwrites[target] = overwrite
        if 'category' in channel_data['type'].lower():
            channel = await guild.create_category(
                name=channel_data['name'],
                overwrites=overwrites,
                position=channel_data['position']
            )
        elif 'voice' in channel_data['type'].lower():
            channel = await guild.create_voice_channel(
                name=channel_data['name'],
                overwrites=overwrites,
                position=channel_data['position'],
                category=category
            )
        else:
            channel = await guild.create_text_channel(
                name=channel_data['name'],
                overwrites=overwrites,
                position=channel_data['position'],
                category=category
            )
        channel_mapping[channel_data['id']] = channel.id
    for message_data in snapshot.get('messages', []):
        channel = guild.get_channel(channel_mapping.get(message_data['channel_id']))
        if channel and isinstance(channel, (nextcord.TextChannel, nextcord.ForumChannel)):
            files = []
            for url in message_data['attachments']:
                file = await download_attachment(url)
                if file:
                    files.append(file)
            embeds = [nextcord.Embed.from_dict(embed) for embed in message_data['embeds']]
            try:
                await channel.send(
                    content=message_data['content'],
                    embeds=embeds,
                    files=files
                )
            except Exception:
                pass  # Handle cases where sending fails
    await interaction.followup.send('Server restoration completed.', ephemeral=True)

async def track_roles(member):
    role_ids = [role.id for role in member.roles if role != member.guild.default_role]
    role_data = {
        'user_id': member.id,
        'guild_id': member.guild.id,
        'roles': role_ids,
        'timestamp': datetime.utcnow()
    }
    await db.user_roles.update_one(
        {'user_id': member.id, 'guild_id': member.guild.id},
        {'$set': role_data},
        upsert=True
    )

async def restore_member_roles(member):
    data = await db.user_roles.find_one({'user_id': member.id, 'guild_id': member.guild.id})
    if data:
        role_ids = data.get('roles', [])
        roles = [member.guild.get_role(role_id) for role_id in role_ids if member.guild.get_role(role_id)]
        if roles:
            await member.add_roles(*roles)

@bot.slash_command(name='verify', description='Verify your Roblox account.')
async def verify(interaction: Interaction):
    await interaction.response.defer(ephemeral=True)
    async def message_check(msg):
        return msg.author == interaction.user and isinstance(msg.channel, nextcord.DMChannel)
    try:
        await interaction.user.send('Please enter your Roblox username:')
        username_msg = await bot.wait_for('message', check=message_check, timeout=60)
        username = username_msg.content.strip()
        async with aiohttp.ClientSession() as session:
            async with session.get(f'https://api.roblox.com/users/get-by-username?username={urllib.parse.quote(username)}') as resp:
                data = await resp.json()
                if 'Id' not in data or data['Id'] == 0:
                    await interaction.user.send('Invalid Roblox username.')
                    return
                roblox_id = data['Id']
            code_phrase = f"Discord Verification Code: {interaction.user.id}"
            await interaction.user.send(f"Please set the following phrase in your Roblox status and reply 'done' when finished:\n\n{code_phrase}")
            response_msg = await bot.wait_for('message', check=message_check, timeout=300)
            if response_msg.content.lower() != 'done':
                await interaction.user.send('Verification cancelled.')
                return
            async with session.get(f'https://users.roblox.com/v1/users/{roblox_id}/status') as status_resp:
                if status_resp.status != 200:
                    await interaction.user.send('Failed to retrieve your Roblox status.')
                    return
                status_data = await status_resp.json()
                status = status_data.get('status', '')
                if code_phrase not in status:
                    await interaction.user.send('Verification phrase not found in your Roblox status.')
                    return
            existing_verification = await db.verifications.find_one({'roblox_id': roblox_id})
            if existing_verification and existing_verification['discord_id'] != interaction.user.id:
                staff_channel = bot.get_channel(STAFF_CHANNEL_ID)
                if staff_channel:
                    embed = nextcord.Embed(title='Alt Account Detected', color=nextcord.Color.red())
                    embed.add_field(name='Discord User', value=interaction.user.mention)
                    embed.add_field(name='Roblox Username', value=username)
                    embed.add_field(name='Roblox ID', value=roblox_id)
                    view = ApplicationReviewView(None, interaction.user.id, 'Alt Account Verification')
                    await staff_channel.send(embed=embed, view=view)
                else:
                    await interaction.user.send('Your verification requires manual approval.')
                return
            total_value = await get_total_value(roblox_id)
            if total_value is None:
                await interaction.user.send('Failed to retrieve your inventory value.')
                return
            guild = bot.get_guild(YOUR_GUILD_ID)
            member = guild.get_member(interaction.user.id)
            assigned_role = None
            if total_value >= 1_000_000:
                assigned_role = guild.get_role(ROLE_ID_FOR_1M)
            elif total_value >= 100_000:
                assigned_role = guild.get_role(ROLE_ID_FOR_100K)
            elif total_value >= 25_000:
                assigned_role = guild.get_role(ROLE_ID_FOR_25K)
            if assigned_role:
                await member.add_roles(assigned_role)
                await interaction.user.send(f'Verification successful! You have been assigned the {assigned_role.name} role.')
                await db.verifications.update_one(
                    {'discord_id': interaction.user.id},
                    {'$set': {'roblox_id': roblox_id, 'total_value': total_value}},
                    upsert=True
                )
            else:
                await interaction.user.send('You do not meet the value requirements for any role.')
        await track_roles(member)
    except asyncio.TimeoutError:
        await interaction.user.send('Verification timed out.')
    except Exception:
        await interaction.user.send('An error occurred during verification.')

@bot.slash_command(name='report', description='Report a scammer or rule violator.')
async def report(interaction: Interaction):
    modal = Modal(title='Scam Report')
    user_id_input = TextInput(label='User ID', placeholder='Enter the User ID', required=True)
    description_input = TextInput(label='What happened?', style=nextcord.TextInputStyle.paragraph, required=True)
    proof_input = TextInput(label='Proof (URLs separated by commas)', placeholder='Enter proof URLs', style=nextcord.TextInputStyle.paragraph, required=False)
    modal.add_item(user_id_input)
    modal.add_item(description_input)
    modal.add_item(proof_input)
    async def modal_callback(modal_interaction):
        user_id = user_id_input.value
        description = description_input.value
        proof = proof_input.value
        proof_urls = [url.strip() for url in proof.split(',') if url.strip()]
        staff_channel = bot.get_channel(STAFF_CHANNEL_ID)
        if staff_channel:
            embed = nextcord.Embed(title='New Scam Report', color=nextcord.Color.orange())
            embed.add_field(name='User ID', value=user_id, inline=False)
            embed.add_field(name='Description', value=description, inline=False)
            if proof_urls:
                embed.add_field(name='Proof', value='\n'.join(proof_urls), inline=False)
            embed.set_footer(text=f'Reported by {interaction.user}', icon_url=interaction.user.avatar.url)
            embed.timestamp = datetime.utcnow()
            view = ApplicationReviewView(None, int(user_id), 'Scammer Report')
            await staff_channel.send(embed=embed, view=view)
            await modal_interaction.response.send_message('Your report has been submitted.', ephemeral=True)
        else:
            await modal_interaction.response.send_message('Failed to submit report. Please contact staff.', ephemeral=True)
    modal.callback = modal_callback
    await interaction.response.send_modal(modal)

@bot.slash_command(name='blacklist', description='Temporarily blacklist a user.')
@commands.has_permissions(manage_roles=True)
async def blacklist(
    interaction: Interaction,
    member: nextcord.Member,
    duration: int = SlashOption(description='Duration in days', choices={"1 day":1,"3 days":3,"7 days":7,"14 days":14}),
    reason: str = SlashOption(description='Reason for blacklisting', required=False, default='No reason provided.')
):
    blacklist_role = interaction.guild.get_role(BLACKLIST_ROLE_ID)
    if not blacklist_role:
        await interaction.response.send_message('Blacklist role not found.', ephemeral=True)
        return
    stocker_roles = [interaction.guild.get_role(role_id) for role_id in STOCKER_ROLE_IDS if interaction.guild.get_role(role_id)]
    member_roles = [role.id for role in member.roles]
    stocker_roles_member = [role for role in stocker_roles if role.id in member_roles]
    await db.blacklists.update_one(
        {'user_id': member.id, 'guild_id': interaction.guild.id},
        {'$set': {
            'unblacklist_time': datetime.utcnow() + timedelta(days=duration),
            'reason': reason,
            'stocker_roles': [role.id for role in stocker_roles_member]
        }},
        upsert=True
    )
    if stocker_roles_member:
        await member.remove_roles(*stocker_roles_member)
    await member.add_roles(blacklist_role)
    await interaction.response.send_message(f'{member.mention} has been blacklisted for {duration} day(s). Reason: {reason}', ephemeral=True)

@tasks.loop(minutes=1)
async def check_blacklists():
    now = datetime.utcnow()
    blacklists = await db.blacklists.find({'unblacklist_time': {'$lte': now}}).to_list(length=100)
    for entry in blacklists:
        guild = bot.get_guild(entry['guild_id'])
        if not guild:
            await db.blacklists.delete_one({'_id': entry['_id']})
            continue
        member = guild.get_member(entry['user_id'])
        if member:
            blacklist_role = guild.get_role(BLACKLIST_ROLE_ID)
            if blacklist_role and blacklist_role in member.roles:
                await member.remove_roles(blacklist_role)
            stocker_roles = [guild.get_role(role_id) for role_id in entry.get('stocker_roles', []) if guild.get_role(role_id)]
            if stocker_roles:
                await member.add_roles(*stocker_roles)
        await db.blacklists.delete_one({'_id': entry['_id']})

@bot.slash_command(name='addnote', description='Add a note to a user.')
@commands.has_permissions(manage_messages=True)
async def addnote(
    interaction: Interaction,
    member: nextcord.Member,
    note: str = SlashOption(description='Note content', required=True)
):
    await db.notes.update_one(
        {'user_id': member.id},
        {'$push': {'notes': {'content': note, 'timestamp': datetime.utcnow(), 'author_id': interaction.user.id}}},
        upsert=True
    )
    await interaction.response.send_message(f'Note added to {member.mention}.', ephemeral=True)

@bot.slash_command(name='getnotes', description='Retrieve notes of a user.')
@commands.has_permissions(manage_messages=True)
async def getnotes(
    interaction: Interaction,
    member: nextcord.Member
):
    data = await db.notes.find_one({'user_id': member.id})
    if data and 'notes' in data:
        notes = data['notes']
        embed = nextcord.Embed(title=f'Notes for {member}', color=nextcord.Color.blue())
        for idx, note in enumerate(notes, 1):
            author = interaction.guild.get_member(note['author_id'])
            author_name = author.display_name if author else 'Unknown'
            embed.add_field(
                name=f'Note {idx}',
                value=f"**Author:** {author_name}\n**Timestamp:** {note['timestamp'].strftime('%Y-%m-%d %H:%M:%S')}\n**Content:** {note['content']}",
                inline=False
            )
        await interaction.response.send_message(embed=embed, ephemeral=True)
    else:
        await interaction.response.send_message('No notes found for this user.', ephemeral=True)

@bot.slash_command(name='apply', description='Apply for a specific role.')
async def apply(interaction: Interaction):
    roles = {
        'Top Stocker': 'top_stocker',
        'Stocker': 'stocker',
        'Small Stocker': 'small_stocker',
        'In-game Stocker': 'in_game_stocker',
        'Robux Stocker': 'robux_stocker',
        'Exchanger': 'exchanger'
    }
    options = [nextcord.SelectOption(label=role, value=value) for role, value in roles.items()]
    select = Select(placeholder='Choose a role to apply for', options=options)
    async def select_callback(select_interaction):
        selected_role = select.values[0]
        await handle_application(select_interaction, selected_role)
    select.callback = select_callback
    view = View()
    view.add_item(select)
    await interaction.response.send_message('Select the role you wish to apply for:', view=view, ephemeral=True)

async def handle_application(interaction, role_type):
    category = nextcord.utils.get(interaction.guild.categories, name='Applications')
    if not category:
        category = await interaction.guild.create_category('Applications')
    channel = await interaction.guild.create_text_channel(
        name=f'application-{interaction.user.name}',
        overwrites={
            interaction.guild.default_role: nextcord.PermissionOverwrite(read_messages=False),
            interaction.user: nextcord.PermissionOverwrite(read_messages=True)
        },
        category=category
    )
    questions = [
        "Please provide a brief introduction about yourself.",
        "What previous experience do you have related to this role?",
        "Why do you want to apply for this role?",
        "Do you meet the minimum requirements? Please provide proof."
    ]
    await channel.send(f'{interaction.user.mention}, thank you for applying for the **{role_type.replace("_", " ").title()}** role!')
    await channel.send('Please answer the following questions:')
    answers = {}
    def check(m):
        return m.author == interaction.user and m.channel == channel
    for question in questions:
        await channel.send(question)
        try:
            msg = await bot.wait_for('message', check=check, timeout=300)
            answers[question] = msg.content
        except asyncio.TimeoutError:
            await channel.send('Application timed out due to inactivity.')
            await interaction.followup.send(f'Your application has been cancelled due to inactivity.', ephemeral=True)
            return
    application_data = {
        'user_id': interaction.user.id,
        'role_type': role_type,
        'answers': answers,
        'timestamp': datetime.utcnow(),
        'status': 'pending'
    }
    result = await db.applications.insert_one(application_data)
    application_id = result.inserted_id
    staff_channel = bot.get_channel(STAFF_CHANNEL_ID)
    if staff_channel:
        embed = nextcord.Embed(title='New Application', color=nextcord.Color.blue())
        embed.set_author(name=interaction.user, icon_url=interaction.user.avatar.url)
        embed.add_field(name='Role Applied', value=role_type.replace("_", " ").title(), inline=False)
        embed.add_field(name='User', value=f'{interaction.user} ({interaction.user.id})', inline=False)
        embed.timestamp = datetime.utcnow()
        view = ApplicationReviewView(application_id, interaction.user.id, role_type)
        await staff_channel.send(embed=embed, view=view)
    await channel.send('Thank you for your application! Our staff team will review it and get back to you soon.')
    await interaction.followup.send(f'Your application has been submitted and a private channel has been created: {channel.mention}', ephemeral=True)

@bot.slash_command(name='schedule_purge', description='Schedule a channel to be purged.')
@commands.has_permissions(manage_channels=True)
async def schedule_purge(interaction: Interaction, channel: nextcord.TextChannel, delay: int = SlashOption(description='Delay in minutes', min_value=1)):
    purge_time = datetime.utcnow() + timedelta(minutes=delay)
    await db.channel_purges.insert_one({
        'channel_id': channel.id,
        'purge_time': purge_time
    })
    await interaction.response.send_message(f'Channel {channel.mention} will be purged in {delay} minute(s).', ephemeral=True)

@tasks.loop(minutes=1)
async def check_purges():
    now = datetime.utcnow()
    purges = await db.channel_purges.find({'purge_time': {'$lte': now}}).to_list(length=100)
    for entry in purges:
        channel = bot.get_channel(entry['channel_id'])
        if channel and isinstance(channel, nextcord.TextChannel):
            try:
                await channel.purge(limit=None)
            except Exception:
                pass  # Handle cases where purging fails
        await db.channel_purges.delete_one({'_id': entry['_id']})

@tasks.loop(hours=24)
async def backup_database():
    try:
        backup_time = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
        backup_file = f'db_backup_{backup_time}.bson'
        os.system(f"mongodump --uri='{MONGO_URI}' --archive='{backup_file}'")
    except Exception:
        pass  # Log exception if needed

@bot.event
async def on_message(message):
    if message.author.bot:
        return
    reaction_channel_ids = [int(id_) for id_ in os.getenv('REACTION_CHANNEL_IDS', '').split(',') if id_.isdigit()]
    if message.channel.id in reaction_channel_ids:
        await message.add_reaction('👍')
        await message.add_reaction('👎')
    strike_channel_id = int(os.getenv('STRIKE_CHANNEL_ID', 0))
    specific_role_name = os.getenv('SPECIFIC_ROLE_NAME', 'Specific Role')
    if message.channel.id == strike_channel_id:
        role_to_ping = get(message.guild.roles, name=specific_role_name)
        if role_to_ping not in message.role_mentions:
            await message.delete()
            strike_role_ids = [int(id_) for id_ in os.getenv('STRIKE_ROLE_IDS', '').split(',') if id_.isdigit()]
            member_strike_roles = [get(message.guild.roles, id=role_id) for role_id in strike_role_ids]
            current_strikes = [role for role in member_strike_roles if role in message.author.roles]
            if len(current_strikes) < 3:
                next_strike_role = member_strike_roles[len(current_strikes)] if len(current_strikes) < len(member_strike_roles) else None
                if next_strike_role:
                    await message.author.add_roles(next_strike_role)
                    await message.author.send(f'You have received a strike for not pinging {role_to_ping.name}.')
            else:
                blacklist_role = get(message.guild.roles, id=BLACKLIST_ROLE_ID)
                if blacklist_role:
                    await message.author.add_roles(blacklist_role)
                    await message.author.send('You have been blacklisted for repeated rule violations.')
                    await db.notes.update_one(
                        {'user_id': message.author.id},
                        {'$push': {'notes': {'content': 'Blacklisted due to 4 strikes.', 'timestamp': datetime.utcnow(), 'author_id': bot.user.id}}},
                        upsert=True
                    )
    await bot.process_commands(message)

@bot.event
async def on_command_error(interaction, error):
    if isinstance(error, commands.MissingPermissions):
        await interaction.response.send_message('You do not have permission to use this command.', ephemeral=True)
    elif isinstance(error, commands.CommandInvokeError):
        await interaction.response.send_message('An error occurred while executing the command.', ephemeral=True)
    else:
        await interaction.response.send_message('An unexpected error occurred.', ephemeral=True)

bot.run(TOKEN) Please code this so everything uses good looking embeds and tell me how the verification works give the whole code and only the code