# ╔══════════════════════════════════════════════════════════════════╗
# ║                                                                  ║
# ║   ░█▀▀░█▀█░█▀▄░█▀▀░█░█   ░█▀▄░█▀▀░█░█░█▀▀                     ║
# ║   ░█░░░█░█░█░█░█▀▀░▄▀▄   ░█░█░█▀▀░▀▄▀░▀▀█                     ║
# ║   ░▀▀▀░▀▀▀░▀▀░░▀▀▀░▀░▀   ░▀▀░░▀▀▀░░▀░░▀▀▀                     ║
# ║                                                                  ║
# ║            © 2026 CodeX Devs — All Rights Reserved              ║
# ║                                                                  ║
# ║   discord  ──  https://discord.gg/codexdev                      ║
# ║   youtube  ──  https://youtube.com/@CodeXDevs                   ║
# ║   github   ──  https://github.com/RayExo                        ║
# ║                                                                  ║
# ╚══════════════════════════════════════════════════════════════════╝

import discord
import aiosqlite
import json
import asyncio
import os
from discord.ext import commands
from typing import Optional

RED_THEME_COLOR = 0xFF0000

class StickyMessage(commands.Cog):
    def __init__(self, bot: commands.Bot):
        self.bot = bot
        asyncio.create_task(self.setup_database())

    async def setup_database(self):
        if not os.path.exists("db"):
            os.makedirs("db")
            
        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute("""
                CREATE TABLE IF NOT EXISTS sticky_messages (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    guild_id INTEGER NOT NULL,
                    channel_id INTEGER NOT NULL,
                    message_type TEXT DEFAULT 'plain',
                    message_content TEXT,
                    embed_data TEXT,
                    last_message_id INTEGER,
                    enabled BOOLEAN DEFAULT 1,
                    delay_seconds INTEGER DEFAULT 2,
                    auto_delete_after INTEGER DEFAULT 0,
                    ignore_bots BOOLEAN DEFAULT 1,
                    ignore_commands BOOLEAN DEFAULT 1,
                    trigger_count INTEGER DEFAULT 1,
                    current_count INTEGER DEFAULT 0,
                    UNIQUE(guild_id, channel_id)
                )
            """)
            await db.commit()

    @commands.Cog.listener()
    async def on_message(self, message: discord.Message):
        if not message.guild or (message.author.bot and message.author.id != self.bot.user.id):
            return

        async with aiosqlite.connect("db/stickymessages.db") as db:
            db.row_factory = aiosqlite.Row
            cursor = await db.execute(
                "SELECT * FROM sticky_messages WHERE channel_id = ? AND enabled = 1",
                (message.channel.id,)
            )
            sticky_data = await cursor.fetchone()

        if not sticky_data:
            return

        if sticky_data['ignore_bots'] and message.author.bot:
            return
            
        if sticky_data['ignore_commands']:
            prefixes = await self.bot.get_prefix(message)
            if isinstance(prefixes, str):
                prefixes = [prefixes]
            if any(message.content.startswith(p) for p in prefixes):
                return

        new_count = sticky_data['current_count'] + 1
        if new_count < sticky_data['trigger_count']:
            async with aiosqlite.connect("db/stickymessages.db") as db:
                await db.execute(
                    "UPDATE sticky_messages SET current_count = ? WHERE id = ?",
                    (new_count, sticky_data['id'])
                )
                await db.commit()
            return

        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute("UPDATE sticky_messages SET current_count = 0 WHERE id = ?", (sticky_data['id'],))
            await db.commit()

        await asyncio.sleep(sticky_data['delay_seconds'])

        if sticky_data['last_message_id']:
            try:
                last_msg = await message.channel.fetch_message(sticky_data['last_message_id'])
                await last_msg.delete()
            except discord.NotFound:
                pass

        content = None
        embed = None
        delete_after = sticky_data['auto_delete_after'] if sticky_data['auto_delete_after'] > 0 else None

        if sticky_data['message_type'] == 'plain':
            content = sticky_data['message_content']
        else:
            try:
                embed_data = json.loads(sticky_data['embed_data'])
                embed = discord.Embed(
                    title=embed_data.get('title'),
                    description=embed_data.get('description'),
                    color=RED_THEME_COLOR
                )
                if footer := embed_data.get('footer'):
                    embed.set_footer(text=footer)
            except (json.JSONDecodeError, ValueError):
                embed = discord.Embed(description="Error: Could not decode embed data.", color=RED_THEME_COLOR)

        new_sticky = await message.channel.send(content=content, embed=embed, delete_after=delete_after)

        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute(
                "UPDATE sticky_messages SET last_message_id = ? WHERE id = ?",
                (new_sticky.id, sticky_data['id'])
            )
            await db.commit()

    @commands.group(aliases=['sticky', 'sm'], invoke_without_command=True)
    @commands.has_permissions(manage_messages=True)
    async def stickymessage(self, ctx: commands.Context):
        if ctx.invoked_subcommand is None:
            await ctx.send_help(ctx.command)

    @stickymessage.command(name='setup')
    @commands.has_permissions(manage_messages=True)
    async def sticky_setup(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
        channel = channel or ctx.channel
        async with aiosqlite.connect("db/stickymessages.db") as db:
            cursor = await db.execute("SELECT 1 FROM sticky_messages WHERE channel_id = ?", (channel.id,))
            if await cursor.fetchone():
                embed = discord.Embed(title="Setup Error", description=f"A sticky message already exists in {channel.mention}.", color=RED_THEME_COLOR)
                return await ctx.send(embed=embed)
        
        embed = discord.Embed(title="Sticky Message Setup", description=f"Choose the message type for {channel.mention}:", color=RED_THEME_COLOR)
        await ctx.send(embed=embed, view=StickySetupView(ctx, channel))

    @stickymessage.command(name='remove', aliases=['delete', 'del'])
    @commands.has_permissions(manage_messages=True)
    async def sticky_remove(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
        channel = channel or ctx.channel
        async with aiosqlite.connect("db/stickymessages.db") as db:
            cursor = await db.execute("SELECT last_message_id FROM sticky_messages WHERE channel_id = ?", (channel.id,))
            data = await cursor.fetchone()
            if not data:
                return await ctx.send(embed=discord.Embed(title="Not Found", description=f"No sticky message found in {channel.mention}.", color=RED_THEME_COLOR))
            
            if data[0]:
                try:
                    msg = await channel.fetch_message(data[0])
                    await msg.delete()
                except discord.NotFound:
                    pass
            
            await db.execute("DELETE FROM sticky_messages WHERE channel_id = ?", (channel.id,))
            await db.commit()

        embed = discord.Embed(title="Success", description=f"Sticky message in {channel.mention} has been removed.", color=RED_THEME_COLOR)
        await ctx.send(embed=embed)

    @stickymessage.command(name='list')
    @commands.has_permissions(manage_messages=True)
    async def sticky_list(self, ctx: commands.Context):
        async with aiosqlite.connect("db/stickymessages.db") as db:
            db.row_factory = aiosqlite.Row
            cursor = await db.execute("SELECT * FROM sticky_messages WHERE guild_id = ?", (ctx.guild.id,))
            stickies = await cursor.fetchall()

        if not stickies:
            return await ctx.send(embed=discord.Embed(title="No Stickies Found", description="There are no active sticky messages on this server.", color=RED_THEME_COLOR))

        embed = discord.Embed(title=f"Sticky Messages in {ctx.guild.name}", color=RED_THEME_COLOR)
        for sticky in stickies:
            channel = self.bot.get_channel(sticky['channel_id'])
            status = "✅ Enabled" if sticky['enabled'] else "❌ Disabled"
            embed.add_field(
                name=f"#{channel.name if channel else 'Unknown Channel'}",
                value=f"**Type**: {sticky['message_type'].title()}\n**Status**: {status}\n**Delay**: {sticky['delay_seconds']}s",
                inline=True
            )
        await ctx.send(embed=embed)

    @stickymessage.command(name='edit')
    @commands.has_permissions(manage_messages=True)
    async def sticky_edit(self, ctx: commands.Context, channel: Optional[discord.TextChannel] = None):
        channel = channel or ctx.channel
        async with aiosqlite.connect("db/stickymessages.db") as db:
            db.row_factory = aiosqlite.Row
            cursor = await db.execute("SELECT * FROM sticky_messages WHERE channel_id = ?", (channel.id,))
            sticky_data = await cursor.fetchone()
        
        if not sticky_data:
            return await ctx.send(embed=discord.Embed(title="Not Found", description=f"No sticky message found in {channel.mention}.", color=RED_THEME_COLOR))

        embed = discord.Embed(title="Edit Sticky Message", description=f"Editing sticky for {channel.mention}. Choose an option:", color=RED_THEME_COLOR)
        await ctx.send(embed=embed, view=StickyEditView(ctx, channel, sticky_data))

class AuthorOnlyView(discord.ui.View):
    def __init__(self, ctx: commands.Context, timeout: float = 300.0):
        super().__init__(timeout=timeout)
        self.ctx = ctx

    async def interaction_check(self, interaction: discord.Interaction) -> bool:
        if interaction.user.id != self.ctx.author.id:
            await interaction.response.send_message("You are not authorized to use this.", ephemeral=True)
            return False
        return True
    
    async def on_timeout(self):
        try:
            for item in self.children:
                item.disabled = True
            if hasattr(self, 'message'):
                await self.message.edit(view=self)
        except discord.NotFound:
            pass

class StickySetupView(AuthorOnlyView):
    def __init__(self, ctx: commands.Context, channel: discord.TextChannel):
        super().__init__(ctx)
        self.channel = channel

    @discord.ui.button(label='Plain Text', style=discord.ButtonStyle.primary, emoji='📝')
    async def plain_text(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.send_modal(PlainTextModal(self.ctx, self.channel))

    @discord.ui.button(label='Embed', style=discord.ButtonStyle.secondary, emoji='📋')
    async def embed_message(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.send_modal(EmbedModal(self.ctx, self.channel))

    @discord.ui.button(label='Cancel', style=discord.ButtonStyle.danger)
    async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
        embed = discord.Embed(title="Cancelled", description="Sticky message setup has been cancelled.", color=RED_THEME_COLOR)
        await interaction.response.edit_message(embed=embed, view=None)

class PlainTextModal(discord.ui.Modal, title='Plain Text Sticky Message'):
    def __init__(self, ctx: commands.Context, channel: discord.TextChannel):
        super().__init__()
        self.ctx = ctx
        self.channel = channel

    message_content = discord.ui.TextInput(label='Message Content', style=discord.TextStyle.long, required=True, max_length=2000)
    delay_seconds = discord.ui.TextInput(label='Delay (seconds)', default='2', required=False, max_length=3)

    async def on_submit(self, interaction: discord.Interaction):
        try:
            delay = int(self.delay_seconds.value or "2")
        except ValueError:
            delay = 2

        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute(
                "INSERT INTO sticky_messages (guild_id, channel_id, message_type, message_content, delay_seconds) VALUES (?, ?, ?, ?, ?)",
                (self.ctx.guild.id, self.channel.id, 'plain', self.message_content.value, delay)
            )
            await db.commit()

        embed = discord.Embed(title="Sticky Created", description=f"Successfully created a plain text sticky in {self.channel.mention}.", color=RED_THEME_COLOR)
        await interaction.response.edit_message(embed=embed, view=None)

class EmbedModal(discord.ui.Modal, title='Embed Sticky Message'):
    def __init__(self, ctx: commands.Context, channel: discord.TextChannel):
        super().__init__()
        self.ctx = ctx
        self.channel = channel

    title = discord.ui.TextInput(label='Embed Title', required=False, max_length=256)
    description = discord.ui.TextInput(label='Embed Description', style=discord.TextStyle.long, required=True, max_length=4000)
    footer = discord.ui.TextInput(label='Embed Footer', required=False, max_length=2048)
    delay_seconds = discord.ui.TextInput(label='Delay (seconds)', default='2', required=False, max_length=3)

    async def on_submit(self, interaction: discord.Interaction):
        try:
            delay = int(self.delay_seconds.value or "2")
        except ValueError:
            delay = 2

        embed_data = {
            "title": self.title.value,
            "description": self.description.value,
            "color": f"#{RED_THEME_COLOR:06x}",
            "footer": self.footer.value
        }

        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute(
                "INSERT INTO sticky_messages (guild_id, channel_id, message_type, embed_data, delay_seconds) VALUES (?, ?, ?, ?, ?)",
                (self.ctx.guild.id, self.channel.id, 'embed', json.dumps(embed_data), delay)
            )
            await db.commit()
        
        embed = discord.Embed(title="Sticky Embed Created", description=f"Successfully created an embed sticky in {self.channel.mention}.", color=RED_THEME_COLOR)
        await interaction.response.edit_message(embed=embed, view=None)

class StickyEditView(AuthorOnlyView):
    def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
        super().__init__(ctx)
        self.channel = channel
        self.sticky_data = sticky_data

    @discord.ui.button(label='Edit Content', style=discord.ButtonStyle.primary)
    async def edit_content(self, interaction: discord.Interaction, button: discord.ui.Button):
        if self.sticky_data['message_type'] == 'plain':
            await interaction.response.send_modal(EditPlainTextModal(self.ctx, self.channel, self.sticky_data))
        else:
            await interaction.response.send_modal(EditEmbedModal(self.ctx, self.channel, self.sticky_data))

    @discord.ui.button(label='Edit Settings', style=discord.ButtonStyle.secondary)
    async def edit_settings(self, interaction: discord.Interaction, button: discord.ui.Button):
        await interaction.response.send_modal(EditSettingsModal(self.ctx, self.channel, self.sticky_data))

    @discord.ui.button(label='Cancel', style=discord.ButtonStyle.danger)
    async def cancel(self, interaction: discord.Interaction, button: discord.ui.Button):
        embed = discord.Embed(title="Cancelled", description="Editing has been cancelled.", color=RED_THEME_COLOR)
        await interaction.response.edit_message(embed=embed, view=None)

class EditPlainTextModal(discord.ui.Modal, title='Edit Plain Text Content'):
    def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
        super().__init__()
        self.ctx = ctx
        self.channel = channel
        self.message_content = discord.ui.TextInput(
            label='Message Content',
            style=discord.TextStyle.long,
            default=sticky_data['message_content'],
            max_length=2000
        )
        self.add_item(self.message_content)

    async def on_submit(self, interaction: discord.Interaction):
        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute(
                "UPDATE sticky_messages SET message_content = ? WHERE channel_id = ?",
                (self.message_content.value, self.channel.id)
            )
            await db.commit()
        embed = discord.Embed(title="Content Updated", description="The sticky message content has been updated.", color=RED_THEME_COLOR)
        await interaction.response.edit_message(embed=embed, view=None)

class EditEmbedModal(discord.ui.Modal, title='Edit Embed Content'):
    def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
        super().__init__()
        self.ctx = ctx
        self.channel = channel
        embed_data = json.loads(sticky_data['embed_data'])
        
        self.title = discord.ui.TextInput(label='Embed Title', default=embed_data.get('title', ''), required=False, max_length=256)
        self.description = discord.ui.TextInput(label='Embed Description', style=discord.TextStyle.long, default=embed_data.get('description', ''), required=True, max_length=4000)
        self.footer = discord.ui.TextInput(label='Embed Footer', default=embed_data.get('footer', ''), required=False, max_length=2048)

        self.add_item(self.title)
        self.add_item(self.description)
        self.add_item(self.footer)

    async def on_submit(self, interaction: discord.Interaction):
        embed_data = {
            "title": self.title.value, "description": self.description.value,
            "color": f"#{RED_THEME_COLOR:06x}", "footer": self.footer.value
        }
        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute(
                "UPDATE sticky_messages SET embed_data = ? WHERE channel_id = ?",
                (json.dumps(embed_data), self.channel.id)
            )
            await db.commit()
        embed = discord.Embed(title="Embed Updated", description="The sticky embed content has been updated.", color=RED_THEME_COLOR)
        await interaction.response.edit_message(embed=embed, view=None)
        
class EditSettingsModal(discord.ui.Modal, title='Edit Sticky Settings'):
    def __init__(self, ctx: commands.Context, channel: discord.TextChannel, sticky_data):
        super().__init__()
        self.ctx = ctx
        self.channel = channel

        self.delay_seconds = discord.ui.TextInput(label='Delay (seconds)', default=str(sticky_data['delay_seconds']), max_length=3)
        self.auto_delete_after = discord.ui.TextInput(label='Auto-delete (seconds, 0=off)', default=str(sticky_data['auto_delete_after']), max_length=4)
        self.trigger_count = discord.ui.TextInput(label='Trigger After X Msgs', default=str(sticky_data['trigger_count']), max_length=2)
        
        self.add_item(self.delay_seconds)
        self.add_item(self.auto_delete_after)
        self.add_item(self.trigger_count)

    async def on_submit(self, interaction: discord.Interaction):
        try:
            delay = int(self.delay_seconds.value)
            auto_del = int(self.auto_delete_after.value)
            trigger = int(self.trigger_count.value)
        except ValueError:
            return await interaction.response.send_message("Please enter valid numbers.", ephemeral=True)

        async with aiosqlite.connect("db/stickymessages.db") as db:
            await db.execute(
                "UPDATE sticky_messages SET delay_seconds = ?, auto_delete_after = ?, trigger_count = ? WHERE channel_id = ?",
                (delay, auto_del, trigger, self.channel.id)
            )
            await db.commit()
        embed = discord.Embed(title="Settings Updated", description="The sticky message settings have been updated.", color=RED_THEME_COLOR)
        await interaction.response.edit_message(embed=embed, view=None)

async def setup(bot: commands.Bot):
    await bot.add_cog(StickyMessage(bot))