import disnake
import re
from disnake.ext import commands
from disnake import ui
import json
import os
import asyncio
from typing import Optional
from src.logger import kuby_logger
import time

# Dictionnaire global pour suivre les renommages de salons
# Format: {channel_id: [timestamp1, timestamp2, ...]}
channel_rename_timestamps = {}

def check_channel_rename_rate_limit(channel_id: int) -> int:
    """Vérifie si le renommage va atteindre la limite de Discord (2 edits / 10 min)."""
    now = time.time()
    if channel_id not in channel_rename_timestamps:
        channel_rename_timestamps[channel_id] = []
    
    timestamps = [ts for ts in channel_rename_timestamps[channel_id] if now - ts < 600]
    channel_rename_timestamps[channel_id] = timestamps
    
    if len(timestamps) >= 2:
        return max(0, int(600 - (now - timestamps[0])))
    return 0

def log_channel_rename(channel_id: int):
    """Enregistre un renommage de salon."""
    now = time.time()
    if channel_id not in channel_rename_timestamps:
        channel_rename_timestamps[channel_id] = []
    
    timestamps = [ts for ts in channel_rename_timestamps[channel_id] if now - ts < 600]
    timestamps.append(now)
    channel_rename_timestamps[channel_id] = timestamps

# Chemins ABSOLUS
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
DATA_DIR = os.path.join(BASE_DIR, "..", "data")
TICKET_WHITELIST_SETTINGS_FILE = os.path.join(DATA_DIR, "ticket_whitelist_settings.json")

# Créer le dossier data/ s'il n'existe pas
os.makedirs(DATA_DIR, exist_ok=True)

def load_settings(guild_id: int) -> dict:
    """Charge les paramètres de ticket pour un serveur"""
    if not os.path.exists(TICKET_WHITELIST_SETTINGS_FILE):
        return {}
    try:
        with open(TICKET_WHITELIST_SETTINGS_FILE, "r", encoding='utf-8') as f:
            return json.load(f).get(str(guild_id), {})
    except (json.JSONDecodeError, FileNotFoundError):
        return {}

def save_settings(guild_id: int, settings: dict) -> None:
    """Sauvegarde les paramètres de ticket"""
    data = {}
    if os.path.exists(TICKET_WHITELIST_SETTINGS_FILE):
        try:
            with open(TICKET_WHITELIST_SETTINGS_FILE, "r", encoding='utf-8') as f:
                data = json.load(f)
        except (json.JSONDecodeError, FileNotFoundError):
            pass
    
    data[str(guild_id)] = settings
    os.makedirs(os.path.dirname(TICKET_WHITELIST_SETTINGS_FILE), exist_ok=True)
    with open(TICKET_WHITELIST_SETTINGS_FILE, "w", encoding='utf-8') as f:
        json.dump(data, f, indent=4, ensure_ascii=False)

async def get_ticket_user_id(channel: disnake.TextChannel) -> Optional[int]:
    """Récupère l'ID du propriétaire du ticket de manière robuste (gère le lag de cache)"""
    import re
    topic = getattr(channel, "topic", None)
    
    # Tentative d'utilisation du cache (plus rapide)
    if topic:
        match = re.search(r"(\d{17,20})", topic)
        if match:
            return int(match.group(1))

    # Si le topic est vide ou ID introuvable, on tente de forcer un refresh via l'API
    try:
        # On utilise fetch_channel pour avoir un objet tout neuf avec le topic à jour
        refreshed_channel = await channel.guild.fetch_channel(channel.id)
        topic = getattr(refreshed_channel, "topic", None)
        if topic:
            match = re.search(r"(\d{17,20})", topic)
            if match:
                return int(match.group(1))
    except:
        pass
            
    return None

class TicketWhitelistConfigView(disnake.ui.View):
    def __init__(self, guild_id: int):
        super().__init__(timeout=60)
        self.guild_id = guild_id

    async def prompt_for_id(self, interaction: disnake.Interaction, prompt_text: str, is_category: bool = False):
        await interaction.response.send_message(prompt_text, ephemeral=True)
        
        def check(m):
            return m.author == interaction.user and m.channel == interaction.channel

        try:
            msg = await interaction.client.wait_for('message', check=check, timeout=30)
            target = None
            if is_category:
                if msg.content.isdigit():
                    target = disnake.utils.get(interaction.guild.categories, id=int(msg.content))
                    if not target:
                        await interaction.followup.send("❌ Catégorie introuvable avec cet ID.", ephemeral=True)
                        return None
                else:
                    await interaction.followup.send("❌ Veuillez envoyer l'ID (les chiffres) de la catégorie.", ephemeral=True)
                    return None
            else:
                if msg.role_mentions:
                    target = msg.role_mentions[0]
                elif msg.content.isdigit():
                    target = interaction.guild.get_role(int(msg.content))
            
            if not target:
                await interaction.followup.send("❌ ID ou mention invalide.", ephemeral=True)
                return None
            
            try: await msg.delete()
            except: pass
            return target
            
        except __import__("asyncio").TimeoutError:
            await interaction.followup.send("❌ Temps écoulé.", ephemeral=True)
            return None

    @ui.button(label="Catégorie Tickets", style=disnake.ButtonStyle.primary, row=0, emoji="📁")
    async def set_category(self, interaction: disnake.Interaction, button: ui.Button):
        category = await self.prompt_for_id(interaction, "Envoie l'ID de la **catégorie** où créer les tickets :", is_category=True)
        if category:
            settings = load_settings(self.guild_id)
            settings["ticket_category_id"] = category.id
            save_settings(self.guild_id, settings)
            await interaction.followup.send(f"✅ Catégorie configurée sur **{category.name}**", ephemeral=True)

    @ui.button(label="Rôle Staff", style=disnake.ButtonStyle.primary, row=0, emoji="🛡️")
    async def set_staff_role(self, interaction: disnake.Interaction, button: ui.Button):
        role = await self.prompt_for_id(interaction, "Mentionne le **rôle Staff** ou envoie son ID :")
        if role:
            settings = load_settings(self.guild_id)
            settings["staff_role_id"] = role.id
            save_settings(self.guild_id, settings)
            await interaction.followup.send(f"✅ Rôle Staff configuré sur **{role.name}**", ephemeral=True)

    @ui.button(label="Rôle Équipe Staff", style=disnake.ButtonStyle.primary, row=0, emoji="👥")
    async def set_team_staff_role(self, interaction: disnake.Interaction, button: ui.Button):
        role = await self.prompt_for_id(interaction, "Mentionne le **rôle Équipe Staff** (à ping pour les nouveaux tickets) ou envoie son ID :")
        if role:
            settings = load_settings(self.guild_id)
            settings["team_staff_role_id"] = role.id
            save_settings(self.guild_id, settings)
            await interaction.followup.send(f"✅ Rôle Équipe Staff configuré sur **{role.name}**", ephemeral=True)

    @ui.button(label="Rôle Attente Oral", style=disnake.ButtonStyle.secondary, row=1, emoji="🎤")
    async def set_attente_oral_role(self, interaction: disnake.Interaction, button: ui.Button):
        role = await self.prompt_for_id(interaction, "Mentionne le rôle **Attente Oral** ou envoie son ID :")
        if role:
            settings = load_settings(self.guild_id)
            settings["attente_oral_role_id"] = role.id
            save_settings(self.guild_id, settings)
            await interaction.followup.send(f"✅ Rôle Attente Oral configuré sur **{role.name}**", ephemeral=True)

    @ui.button(label="Rôle Candidature à faire", style=disnake.ButtonStyle.secondary, row=1, emoji="📝")
    async def set_candidature_a_faire_role(self, interaction: disnake.Interaction, button: ui.Button):
        role = await self.prompt_for_id(interaction, "Mentionne le rôle **Candidature à faire** ou envoie son ID :")
        if role:
            settings = load_settings(self.guild_id)
            settings["candidature_a_faire_role_id"] = role.id
            save_settings(self.guild_id, settings)
            await interaction.followup.send(f"✅ Rôle Candidature à faire configuré sur **{role.name}**", ephemeral=True)

    @ui.button(label="Rôle Citoyen", style=disnake.ButtonStyle.success, row=1, emoji="🏙️")
    async def set_citoyen_role(self, interaction: disnake.Interaction, button: ui.Button):
        role = await self.prompt_for_id(interaction, "Mentionne le rôle **Citoyen** ou envoie son ID :")
        if role:
            settings = load_settings(self.guild_id)
            settings["citoyen_role_id"] = role.id
            save_settings(self.guild_id, settings)
            await interaction.followup.send(f"✅ Rôle Citoyen configuré sur **{role.name}**", ephemeral=True)


class ContinueFormView(disnake.ui.View):
    """Vue avec un bouton pour continuer à l'étape suivante"""

    def __init__(self, current_step, step1_data=None, step2_part1_data=None, step2_part2_data=None):
        super().__init__(timeout=180.0)  # Définir un timeout pour la vue
        self.current_step = current_step
        self.step1_data = step1_data
        self.step2_part1_data = step2_part1_data
        self.step2_part2_data = step2_part2_data

    @disnake.ui.button(label="📋 Continuer le formulaire", style=disnake.ButtonStyle.green, custom_id="continue_form")
    async def continue_form(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        """Passe à l'étape suivante du formulaire"""
        if self.current_step == 1:
            await interaction.response.send_modal(TicketStep2Part1(self.step1_data))
        elif self.current_step == 2:
            await interaction.response.send_modal(TicketStep2Part2(self.step1_data, self.step2_part1_data))
        elif self.current_step == 3:
            await interaction.response.send_modal(TicketStep3(self.step1_data, self.step2_part1_data, self.step2_part2_data))

        # Vérifier si le message existe toujours avant de le modifier
        try:
            await interaction.message.edit(view=self)
        except disnake.errors.NotFound:
            # Si le message n'existe plus, nous ne faisons rien
            pass

class TicketStep1(disnake.ui.Modal):
    """Étape 1 du formulaire - Infos personnelles"""

    def __init__(self):
        super().__init__(
            title="📋 Informations Personnelles",
            custom_id="ticket_step1",
            components=[
                disnake.ui.TextInput(label="Pseudo Epic (Fortnite)", custom_id="pseudo", required=True, max_length=16),
                disnake.ui.TextInput(label="Âge", custom_id="age", required=True, max_length=2),
                disnake.ui.TextInput(label="Expérience RP ?", custom_id="experience", style=disnake.TextInputStyle.paragraph, required=True, max_length=50),
                disnake.ui.TextInput(label="Comment as-tu découvert le serveur ?", custom_id="decouverte", required=True, max_length=20),
                disnake.ui.TextInput(label="As-tu un micro de qualité ?", custom_id="micro", required=True, max_length=3),
            ]
        )

    async def callback(self, interaction: disnake.ModalInteraction):
        """Envoie un bouton pour passer à l'étape 2"""
        view = ContinueFormView(current_step=1, step1_data=interaction.text_values)
        await interaction.response.send_message("✅ Première étape terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True)

class TicketStep2Part1(disnake.ui.Modal):
    """Étape 2 du formulaire - Infos RP (Partie 1)"""

    def __init__(self, step1_data):
        self.step1_data = step1_data
        super().__init__(
            title="👤 Infos sur ton personnage (Partie 1)",
            custom_id="ticket_step2_part1",
            components=[
                disnake.ui.TextInput(label="Prénom", custom_id="prenom", required=True),
                disnake.ui.TextInput(label="Nom", custom_id="nom", required=True),
                disnake.ui.TextInput(label="Âge du personnage", custom_id="age_perso", required=True),
                disnake.ui.TextInput(label="Genre", custom_id="genre", required=True, max_length=5),
                disnake.ui.TextInput(label="Date et lieu de naissance", custom_id="naissance", required=True),
            ]
        )

    async def callback(self, interaction: disnake.ModalInteraction):
        """Envoie un bouton pour passer à l'étape suivante"""
        view = ContinueFormView(current_step=2, step1_data=self.step1_data, step2_part1_data=interaction.text_values)
        await interaction.response.send_message("✅ Deuxième partie terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True)

class TicketStep2Part2(disnake.ui.Modal):
    """Étape 2 du formulaire - Infos RP (Partie 2)"""

    def __init__(self, step1_data, step2_part1_data):
        self.step1_data = step1_data
        self.step2_part1_data = step2_part1_data
        super().__init__(
            title="👤 Infos sur ton personnage (Partie 2)",
            custom_id="ticket_step2_part2",
            components=[
                disnake.ui.TextInput(label="Traits de caractère", custom_id="traits", required=True),
                disnake.ui.TextInput(label="Nationalité", custom_id="nationalite", required=True),
                disnake.ui.TextInput(label="Skin RP", custom_id="skin_rp", required=True),
                disnake.ui.TextInput(label="Métier souhaité", custom_id="metier_souhaite", required=True),
            ]
        )

    async def callback(self, interaction: disnake.ModalInteraction):
        """Envoie un bouton pour passer à l'étape 3"""
        view = ContinueFormView(current_step=3, step1_data=self.step1_data, step2_part1_data=self.step2_part1_data, step2_part2_data=interaction.text_values)
        await interaction.response.send_message("✅ Deuxième étape terminée ! Cliquez ci-dessous pour continuer.", view=view, ephemeral=True)

class TicketStep3(disnake.ui.Modal):
    """Étape 3 du formulaire - Histoire du personnage"""

    def __init__(self, step1_data, step2_part1_data, step2_part2_data):
        self.step1_data = step1_data
        self.step2_part1_data = step2_part1_data
        self.step2_part2_data = step2_part2_data
        super().__init__(
            title="📋 Développement du Personnage",
            custom_id="ticket_step3",
            components=[
                disnake.ui.TextInput(
                    label="Histoire du personnage",
                    custom_id="histoire",
                    style=disnake.TextInputStyle.paragraph,
                    required=True,
                    min_length=425,
                    max_length=500
                ),
                disnake.ui.TextInput(label="Objectifs RP (court et long terme)", custom_id="objectifs", style=disnake.TextInputStyle.paragraph, required=True, max_length=100),
            ]
        )

    async def callback(self, interaction: disnake.ModalInteraction):
        """Création du ticket après validation"""
        guild = interaction.guild
        user = interaction.user
        
        settings = load_settings(guild.id)
        category_id = settings.get("ticket_category_id")
        staff_role_id = settings.get("staff_role_id")
        team_staff_role_id = settings.get("team_staff_role_id")

        if not category_id or not staff_role_id or not team_staff_role_id:
            return await interaction.response.send_message("❌ Erreur : Le système de tickets n'est pas entièrement configuré sur ce serveur (Catégorie, Rôle Staff ou Rôle Équipe Staff manquant).", ephemeral=True)

        category = disnake.utils.get(guild.categories, id=int(category_id))
        if category is None:
            return await interaction.response.send_message("❌ Erreur : La catégorie des tickets configurée n'existe plus !", ephemeral=True)

        staff_role = disnake.utils.get(guild.roles, id=int(staff_role_id))
        team_staff_role = disnake.utils.get(guild.roles, id=int(team_staff_role_id))
        
        if staff_role is None or team_staff_role is None:
            return await interaction.response.send_message("❌ Erreur : L'un des rôles staff configurés n'existe plus !", ephemeral=True)

        # Vérifier si l'utilisateur a déjà un ticket ouvert
        for channel in category.text_channels:
            if channel.topic == f"Ticket de {user.id}":
                return await interaction.response.send_message(f"❌ Vous avez déjà un ticket ouvert : {channel.mention}", ephemeral=True)

        # Définir les permissions du salon du ticket
        overwrites = {
            guild.default_role: disnake.PermissionOverwrite(read_messages=False),
            user: disnake.PermissionOverwrite(read_messages=True, send_messages=True),
            staff_role: disnake.PermissionOverwrite(read_messages=True, send_messages=True),
            team_staff_role: disnake.PermissionOverwrite(read_messages=True, send_messages=False)
        }

        # Créer un salon de ticket avec le nom [pseudo]-écrit
        ticket_channel = await category.create_text_channel(
            name=f"{user.name}-écrit",
            topic=f"Ticket de {user.id}",
            overwrites=overwrites
        )

        # Création de l'embed avec les infos du formulaire
        embed = disnake.Embed(title="📩 Ticket Whitelist", color=0x00ff00)
        
        # Helper to get value from data
        s1 = self.step1_data
        s2p1 = self.step2_part1_data
        s2p2 = self.step2_part2_data
        s3 = interaction.text_values

        embed.add_field(name="📋 - Informations Personnelles", value=(
            f"> - Pseudo Epic : {s1['pseudo']}\n"
            f"> - Âge : {s1['age']}\n"
            f"> - Expérience RP : {s1['experience']}\n"
            f"> - Découverte du serveur : {s1['decouverte']}\n"
            f"> - Micro de qualité : {s1['micro']}"
        ), inline=False)
        embed.add_field(name="👤 - Informations RP", value=(
            f"> - Prénom : {s2p1['prenom']}\n"
            f"> - Nom : {s2p1['nom']}\n"
            f"> - Âge du personnage : {s2p1['age_perso']}\n"
            f"> - Genre : {s2p1['genre']}\n"
            f"> - Naissance : {s2p1['naissance']}\n"
            f"> - Traits de caractère : {s2p2['traits']}\n"
            f"> - Nationalité : {s2p2['nationalite']}\n"
            f"> - Skin RP : {s2p2['skin_rp']}\n"
            f"> - Métier souhaité : {s2p2['metier_souhaite']}"
        ), inline=False)
        embed.add_field(name="📋 - Développement du Personnage", value=(
            f"> - Histoire : {s3['histoire']}\n"
            f"> - Objectifs : {s3['objectifs']}"
        ), inline=False)

        # Envoyer l'embed avec les boutons d'actions pour les staff
        await ticket_channel.send(embed=embed, view=TicketManagementView())

        # Envoyer un message pour mentionner l'équipe staff
        await ticket_channel.send(f"{team_staff_role.mention} - Nouveau ticket créé !")

        # Mettre à jour le pseudo du joueur si le bot a les permissions nécessaires
        if guild.me.guild_permissions.manage_nicknames:
            new_nickname = f"{s2p1['prenom']} {s2p1['nom']} | {s1['pseudo']}"
            if len(new_nickname) > 32:
                new_nickname = new_nickname[:32]
            try: await user.edit(nick=new_nickname)
            except disnake.Forbidden: pass
        
        # Envoyer un message de confirmation
        try:
            await interaction.response.send_message(f"✅ Ticket ouvert : {ticket_channel.mention}", ephemeral=True)
        except disnake.errors.NotFound:
            await interaction.followup.send(f"✅ Ticket ouvert : {ticket_channel.mention}", ephemeral=True)

class TicketManagementView(disnake.ui.View):
    """Vue pour gérer le ticket avec les boutons Fermer, Réouvrir, Claim et Supprimer"""

    def __init__(self):
        super().__init__(timeout=None)

    async def get_ticket_owner(self, interaction: disnake.Interaction):
        """Récupère le propriétaire du ticket via le helper robuste"""
        owner_id = await get_ticket_user_id(interaction.channel)
        if owner_id:
            try:
                member = interaction.guild.get_member(owner_id)
                if not member:
                    member = await interaction.guild.fetch_member(owner_id)
                return member
            except:
                return None
        return None

    @disnake.ui.button(label="🔒 Fermer le Ticket", style=disnake.ButtonStyle.red, custom_id="close_ticket")
    async def close_ticket(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        """Ferme le ticket en désactivant l'envoi de messages pour le membre"""
        await interaction.response.defer(ephemeral=True)
        
        settings = load_settings(interaction.guild.id)
        staff_role_id = settings.get("staff_role_id")
        
        is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False
        if not is_staff and not interaction.user.guild_permissions.administrator:
            return await interaction.followup.send("❌ Seuls les staffs peuvent fermer un ticket.", ephemeral=True)

        owner = await self.get_ticket_owner(interaction)
        if not owner:
            return await interaction.followup.send("❌ Impossible de trouver le propriétaire du ticket à fermer.", ephemeral=True)

        try:
            await interaction.channel.set_permissions(owner, read_messages=False, send_messages=False)
            log_channel_rename(interaction.channel.id)
            asyncio.create_task(interaction.channel.edit(name=f"fermé-{owner.display_name}".replace(" ", "-").lower()[:100]))
            
            # Envoi du message de fermeture dans le salon
            new_view = ClosedTicketView()
            await interaction.channel.send(f"🔒 Ce ticket a été fermé par {interaction.user.mention}.", view=new_view)
            await interaction.followup.send("✅ Ticket fermé avec succès.", ephemeral=True)
        except disnake.errors.RateLimited as e:
            await interaction.followup.send(f"⚠️ Limite de débit Discord : Réessayez dans {e.retry_after:.1f}s.", ephemeral=True)
        except Exception as e:
            kuby_logger.error(f"Erreur lors de la fermeture du ticket: {e}")
            await interaction.followup.send(f"❌ Erreur lors de la fermeture : {e}", ephemeral=True)

    @disnake.ui.button(label="👤 Claim le Ticket", style=disnake.ButtonStyle.blurple, custom_id="claim_ticket")
    async def claim_ticket(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        """Un staff prend en charge le ticket"""
        await interaction.response.defer(ephemeral=False)
        
        settings = load_settings(interaction.guild.id)
        staff_role_id = settings.get("staff_role_id")
        
        is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False
        if not is_staff and not interaction.user.guild_permissions.administrator:
            return await interaction.followup.send("❌ Seuls les staffs peuvent claim un ticket.")

        try:
            staff_role = interaction.guild.get_role(int(staff_role_id)) if staff_role_id else None
            
            # Retrait des permissions du rôle staff pour forcer le claimer à parler seul
            if staff_role:
                await interaction.channel.set_permissions(staff_role, send_messages=False, read_messages=True)
            
            # Donner les permissions au claimer
            await interaction.channel.set_permissions(interaction.user, read_messages=True, send_messages=True)
            await interaction.followup.send(f"👥 {interaction.user.mention} a pris en charge ce ticket.")
        except Exception as e:
            kuby_logger.error(f"Erreur lors du claim du ticket: {e}")
            await interaction.followup.send(f"❌ Erreur lors du claim : {e}")

class ClosedTicketView(disnake.ui.View):
    """Vue pour les tickets fermés avec les boutons Réouvrir et Supprimer"""

    def __init__(self):
        super().__init__(timeout=None)

    async def get_ticket_owner(self, interaction: disnake.Interaction):
        """Récupère le propriétaire du ticket via le helper robuste"""
        owner_id = await get_ticket_user_id(interaction.channel)
        if owner_id:
            try:
                member = interaction.guild.get_member(owner_id)
                if not member:
                    member = await interaction.guild.fetch_member(owner_id)
                return member
            except:
                return None
        return None

    @disnake.ui.button(label="♻ Réouvrir le Ticket", style=disnake.ButtonStyle.green, custom_id="reopen_ticket")
    async def reopen_ticket(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        """Réouvre le ticket pour le membre"""
        await interaction.response.defer(ephemeral=True)
        
        settings = load_settings(interaction.guild.id)
        staff_role_id = settings.get("staff_role_id")
        
        is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False
        if not is_staff and not interaction.user.guild_permissions.administrator:
            return await interaction.followup.send("❌ Seuls les staffs peuvent réouvrir un ticket.", ephemeral=True)

        owner = await self.get_ticket_owner(interaction)
        if not owner:
            return await interaction.followup.send("❌ Impossible de trouver le propriétaire du ticket à réouvrir.", ephemeral=True)
            
        try:
            # On demande au staff quel statut remettre
            embed = disnake.Embed(
                title="♻️ Réouverture du Ticket",
                description=f"Choisissez le statut à appliquer pour **{owner.display_name}**.",
                color=disnake.Color.blue()
            )
            view = ReopenStatusView(owner)
            await interaction.channel.send(embed=embed, view=view)
            await interaction.followup.send("✅ Choisissez le statut ci-dessous.", ephemeral=True)
        except Exception as e:
            kuby_logger.error(f"Erreur lors de la préparation de réouverture: {e}")
            await interaction.followup.send(f"❌ Erreur : {e}", ephemeral=True)

class ReopenStatusView(disnake.ui.View):
    """Vue pour choisir le statut lors d'une réouverture"""
    def __init__(self, owner: disnake.Member):
        super().__init__(timeout=180)
        self.owner = owner

    @disnake.ui.button(label="Écrit (Ouvert)", style=disnake.ButtonStyle.secondary)
    async def set_written(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        await self.apply_status(interaction, "ouvert")

    @disnake.ui.button(label="Oral (À faire)", style=disnake.ButtonStyle.primary)
    async def set_oral(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        await self.apply_status(interaction, "oral-à-faire")

    @disnake.ui.button(label="Accepté", style=disnake.ButtonStyle.success)
    async def set_accepted(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        await self.apply_status(interaction, "accepté")

    async def apply_status(self, interaction: disnake.Interaction, prefix: str):
        await interaction.response.defer(ephemeral=True)
        try:
            settings = load_settings(interaction.guild.id)
            candidature_a_faire_role_id = settings.get("candidature_a_faire_role_id")
            attente_oral_role_id = settings.get("attente_oral_role_id")
            citoyen_role_id = settings.get("citoyen_role_id")

            # On récupère les objets rôles
            role_écrit = interaction.guild.get_role(int(candidature_a_faire_role_id)) if candidature_a_faire_role_id else None
            role_oral = interaction.guild.get_role(int(attente_oral_role_id)) if attente_oral_role_id else None
            role_citoyen = interaction.guild.get_role(int(citoyen_role_id)) if citoyen_role_id else None

            # Synchronisation des RÔLES en fonction du statut choisi
            roles_to_remove = []
            role_to_add = None

            if prefix == "ouvert":
                role_to_add = role_écrit
                roles_to_remove = [r for r in [role_oral, role_citoyen] if r]
            elif prefix == "oral-à-faire":
                role_to_add = role_oral
                roles_to_remove = [r for r in [role_écrit, role_citoyen] if r]
            elif prefix == "accepté":
                role_to_add = role_citoyen
                roles_to_remove = [r for r in [role_écrit, role_oral] if r]

            # Mise à jour des rôles sur le membre
            if roles_to_remove:
                await self.owner.remove_roles(*roles_to_remove, reason="Réouverture du ticket - Maj Statut")
            if role_to_add:
                await self.owner.add_roles(role_to_add, reason="Réouverture du ticket - Maj Statut")

            # Mise à jour du SALON
            await interaction.channel.set_permissions(self.owner, read_messages=True, send_messages=True)
            new_name = f"{prefix}-{self.owner.display_name}".replace(" ", "-").lower()[:100]
            log_channel_rename(interaction.channel.id)
            asyncio.create_task(interaction.channel.edit(name=new_name, topic=f"Ticket de {self.owner.id}"))
            
            new_view = TicketManagementView()
            await interaction.channel.send(f"✅ Ticket réouvert par {interaction.user.mention} (Statut: **{prefix}**).", view=new_view)
            await interaction.followup.send(f"✅ Statut **{prefix}** appliqué et rôles synchronisés.", ephemeral=True)
            self.stop()
        except Exception as e:
            kuby_logger.error(f"Erreur lors de la réouverture/synchro: {e}")
            await interaction.followup.send(f"❌ Erreur système : {e}", ephemeral=True)

    @disnake.ui.button(label="❌ Supprimer le Ticket", style=disnake.ButtonStyle.danger, custom_id="delete_ticket")
    async def delete_ticket(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        """Supprime le salon du ticket"""
        await interaction.response.defer(ephemeral=True)
        
        settings = load_settings(interaction.guild.id)
        staff_role_id = settings.get("staff_role_id")
        
        is_staff = any(r.id == int(staff_role_id) for r in interaction.user.roles) if staff_role_id else False
        if not is_staff and not interaction.user.guild_permissions.administrator:
            return await interaction.followup.send("❌ Seuls les staffs peuvent supprimer un ticket.", ephemeral=True)
            
        try:
            await interaction.followup.send("❌ Suppression du ticket...", ephemeral=True)
            await interaction.channel.delete()
        except Exception as e:
            kuby_logger.error(f"Erreur lors de la suppression du ticket: {e}")
            try: await interaction.followup.send(f"❌ Erreur lors de la suppression : {e}", ephemeral=True)
            except: pass

class TicketView(disnake.ui.View):
    """Vue avec le bouton pour ouvrir le ticket"""

    def __init__(self):
        super().__init__(timeout=None)

    @disnake.ui.button(label="📑 Ouvrir un Ticket Whitelist", style=disnake.ButtonStyle.red, custom_id="open_ticket")
    async def open_ticket(self, button: disnake.ui.Button, interaction: disnake.Interaction):
        """Affiche le premier formulaire"""
        await interaction.response.send_modal(TicketStep1())

class Ticket(commands.Cog):
    """Commande pour envoyer l'embed de ticket"""

    def __init__(self, bot):
        self.bot = bot

    async def cog_load(self):
        """Enregistre les vues persistantes"""
        self.bot.add_view(TicketView())
        self.bot.add_view(TicketManagementView())
        self.bot.add_view(ClosedTicketView())

    @commands.slash_command(name="ticket_whitelist", description="Envoie l'embed pour ouvrir un ticket de whitelist.")
    @commands.has_permissions(administrator=True)
    async def ticket_whitelist(self, interaction: disnake.ApplicationCommandInteraction):
        """Envoie l'embed avec le bouton d'ouverture de ticket"""
        embed = disnake.Embed(
            title="📩 Ticket Whitelist",
            description="Pour réaliser votre whitelist, ouvrez un ticket et remplissez le formulaire.",
            color=0x00ff00
        )
        view = TicketView()
        await interaction.channel.send(embed=embed, view=view)
        await interaction.response.send_message("✅ Embed des tickets envoyé.", ephemeral=True)

    @commands.slash_command(name="ticket_whitelist_config", description="Configurer le système de tickets whitelist (Admin)")
    @commands.has_permissions(administrator=True)
    async def ticket_whitelist_config(self, interaction: disnake.ApplicationCommandInteraction):
        """Interface de configuration des tickets"""
        settings = load_settings(interaction.guild.id)
        
        embed = disnake.Embed(
            title="⚙️ Configuration Tickets Whitelist",
            description="Utilisez les boutons ci-dessous pour lier la catégorie et les rôles.",
            color=disnake.Color.blue()
        )
        
        cat_id = settings.get("ticket_category_id")
        s_role_id = settings.get("staff_role_id")
        ts_role_id = settings.get("team_staff_role_id")
        ao_role_id = settings.get("attente_oral_role_id")
        caf_role_id = settings.get("candidature_a_faire_role_id")
        c_role_id = settings.get("citoyen_role_id")
        
        embed.add_field(name="📁 Catégorie Tickets", value=f"Id: {cat_id}" if cat_id else "❌ Non configuré", inline=True)
        embed.add_field(name="🛡️ Staff", value=f"<@&{s_role_id}>" if s_role_id else "❌ Non configuré", inline=True)
        embed.add_field(name="👥 Équipe Staff", value=f"<@&{ts_role_id}>" if ts_role_id else "❌ Non configuré", inline=True)
        embed.add_field(name="🎤 Attente Oral", value=f"<@&{ao_role_id}>" if ao_role_id else "❌ Non configuré", inline=True)
        embed.add_field(name="📝 Candidature à faire", value=f"<@&{caf_role_id}>" if caf_role_id else "❌ Non configuré", inline=True)
        embed.add_field(name="🏙️ Citoyen", value=f"<@&{c_role_id}>" if c_role_id else "❌ Non configuré", inline=True)
        
        await interaction.response.send_message(embed=embed, view=TicketWhitelistConfigView(interaction.guild.id), ephemeral=True)

    @commands.Cog.listener()
    async def on_message(self, message):
        """Surveille les messages des staffs dans les tickets pour validation automatique"""
        if message.guild is None or message.author.bot:
            return
            
        # On ne traite que si le message contient "validé"
        if "validé" not in message.content.lower():
            return

        # Détection du format du topic du ticket via helper (robuste)
        user_id = await get_ticket_user_id(message.channel)
        if not user_id:
            await message.channel.send("❌ Debug: Impossible de récupérer l'ID utilisateur via le topic (même après refresh API).")
            return

        settings = load_settings(message.guild.id)
        staff_role_id = settings.get("staff_role_id")
        
        # Vérification si l'auteur est staff ou admin
        is_staff = False
        if staff_role_id:
            is_staff = any(r.id == int(staff_role_id) for r in message.author.roles)
        
        if not is_staff and not message.author.guild_permissions.administrator:
            if "validé" in message.content.lower():
                await message.channel.send("❌ Debug: Permission refusée. Vous n'êtes ni Staff ni Admin.")
            return

        try:
            # L'ID est déjà trouvé par le helper ci-dessus
            user_id = user_id
            try:
                user = await message.guild.fetch_member(user_id)
            except (disnake.NotFound, disnake.HTTPException):
                await message.channel.send("❌ Utilisateur introuvable. A-t-il quitté le serveur ?", delete_after=10)
                return

            attente_oral_role_id = settings.get("attente_oral_role_id")
            candidature_a_faire_role_id = settings.get("candidature_a_faire_role_id")
            citoyen_role_id = settings.get("citoyen_role_id")

            if not attente_oral_role_id or not candidature_a_faire_role_id or not citoyen_role_id:
                await message.channel.send("❌ Erreur de configuration : Impossible d'attribuer les rôles, ils ne sont pas tous définis dans `/ticket_whitelist_config`.", delete_after=10)
                return

            attente_oral_role = message.guild.get_role(int(attente_oral_role_id))
            candidature_a_faire_role = message.guild.get_role(int(candidature_a_faire_role_id))
            citoyen_role = message.guild.get_role(int(citoyen_role_id))

            if (attente_oral_role and attente_oral_role in user.roles) or "oral-à-faire" in message.channel.name.lower():
                # DEUXIÈME VALIDATION -> ACCEPTÉ / CITOYEN
                if attente_oral_role: await user.remove_roles(attente_oral_role)
                if citoyen_role: await user.add_roles(citoyen_role)
                
                # Mise à jour du nom du salon
                new_name = f"accepté-{user.display_name}".replace(" ", "-").lower()[:100]
                wait_time = check_channel_rename_rate_limit(message.channel.id)
                log_channel_rename(message.channel.id)
                asyncio.create_task(message.channel.edit(name=new_name))
                
                # Confirmation dans le salon (Feedback visuel)
                embed_val = disnake.Embed(
                    title="🎉 Whitelist Validée !",
                    description=f"Félicitations {user.mention}, ton entretien oral a été validé par {message.author.mention} !\nTu es désormais **Citoyen**.",
                    color=disnake.Color.green()
                )
                await message.channel.send(embed=embed_val)
                
                # DM au joueur (RESTAURATION DESIGN CAPTURE)
                try:
                    embed_dm = disnake.Embed(
                        title="🎉 Félicitations - Citoyenneté Validée !",
                        description=f"Nous avons le plaisir de t'annoncer que ton passage sur **{message.guild.name}** a été validé avec succès !",
                        color=disnake.Color.green()
                    )
                    embed_dm.add_field(name="🏙️ Nouveau Statut", value="Citoyen", inline=True)
                    embed_dm.add_field(name="📍 Serveur", value=message.guild.name, inline=True)
                    embed_dm.add_field(name="📋 Prochaine étape", value="Tu peux désormais te connecter au serveur et commencer ton aventure RP. N'oublie pas de consulter les salons d'informations !", inline=False)
                    if wait_time > 0:
                        m, s = divmod(wait_time, 60)
                        embed_dm.add_field(name="⏳ Nom du ticket", value=f"Le nom de ton ticket sera actualisé d'ici {m}m et {s}s environ (limite de sécurité Discord).", inline=False)
                    embed_dm.add_field(name="🔗 Lien vers ton ticket", value=f"[Clique ici pour voir ton ticket]({message.channel.jump_url})", inline=False)
                    embed_dm.set_footer(text=f"L'équipe {message.guild.name}")
                    if message.guild.icon: embed_dm.set_thumbnail(url=message.guild.icon.url)
                    
                    await user.send(embed=embed_dm)
                except: pass

            else:
                # PREMIÈRE VALIDATION -> ORAL À FAIRE
                if candidature_a_faire_role: await user.remove_roles(candidature_a_faire_role)
                if attente_oral_role: await user.add_roles(attente_oral_role)
                
                # Mise à jour du nom du salon
                new_name = f"oral-à-faire-{user.display_name}".replace(" ", "-").lower()[:100]
                wait_time = check_channel_rename_rate_limit(message.channel.id)
                log_channel_rename(message.channel.id)
                asyncio.create_task(message.channel.edit(name=new_name))
                
                # Confirmation dans le salon
                embed_val = disnake.Embed(
                    title="📝 Étape Écrite Validée !",
                    description=f"Ta candidature écrite a été validée par {message.author.mention}.\nTu passes désormais en **Attente Oral**.",
                    color=disnake.Color.orange()
                )
                await message.channel.send(embed=embed_val)
                
                # DM au joueur (RESTAURATION DESIGN CAPTURE)
                try:
                    embed_dm = disnake.Embed(
                        title="📝 Validation Étape Écrite",
                        description=f"Ta candidature écrite sur **{message.guild.name}** a été examinée et validée par notre équipe !",
                        color=disnake.Color.orange()
                    )
                    embed_dm.add_field(name="🎤 Statut Actuel", value="Attente Oral", inline=True)
                    embed_dm.add_field(name="🛠️ Action Requise", value="Merci de te rendre dans le salon vocal 'Attente Oral' dès que tu es disponible pour passer ton entretien.", inline=False)
                    if wait_time > 0:
                        m, s = divmod(wait_time, 60)
                        embed_dm.add_field(name="⏳ Nom du ticket", value=f"Le nom de ton ticket sera actualisé d'ici {m}m et {s}s environ (limite de sécurité Discord).", inline=False)
                    embed_dm.add_field(name="🔗 Lien vers ton ticket", value=f"[Clique ici pour voir ton ticket]({message.channel.jump_url})", inline=False)
                    embed_dm.set_footer(text=f"L'équipe {message.guild.name}")
                    if message.guild.icon: embed_dm.set_thumbnail(url=message.guild.icon.url)

                    await user.send(embed=embed_dm)
                except: pass

        except Exception as e:
            kuby_logger.error(f"Erreur lors de la validation du ticket: {e}", exc_info=True)
            await message.channel.send(f"❌ Une erreur système est survenue : {e}")

def setup(bot):
    bot.add_cog(Ticket(bot))
    bot.add_view(TicketView())
    bot.add_view(TicketManagementView())
    bot.add_view(ClosedTicketView())