package fr.luc.gamingcore.manager;

import fr.luc.gamingcore.Main;
import fr.luc.gamingcore.database.GuildRepository;
import fr.luc.gamingcore.economy.ChunkPriceCalculator;
import fr.luc.gamingcore.economy.EconomyManager;
import fr.luc.gamingcore.model.*;
import fr.luc.gamingcore.util.MessageManager;
import fr.luc.gamingcore.util.TextUtil;
import fr.luc.gamingcore.worldguard.WorldGuardManager;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Service métier central et autonome pour la gestion des guildes, de leurs territoires,
 * de leurs finances, de leurs rangs et de leur cycle de vie complet.
 */
public class GuildManager {

    private final Main plugin;
    private final GuildRepository repository;
    private final EconomyManager economyManager;
    private final WorldGuardManager worldGuardManager;
    private final ChunkPriceCalculator priceCalculator;
    private final InviteManager inviteManager;
    private final MessageManager messageManager;

    // Indexations en mémoire pour des lookups O(1)
    private final Map<UUID, Guild> guildsById = new ConcurrentHashMap<>();
    private final Map<String, UUID> guildsByName = new ConcurrentHashMap<>();
    private final Map<UUID, UUID> playerGuildMap = new ConcurrentHashMap<>();
    private final Map<String, UUID> chunkGuildMap = new ConcurrentHashMap<>();

    /**
     * Constructeur complet du gestionnaire de guilde avec WorldGuard.
     *
     * @param plugin            Instance principale du plugin.
     * @param repository        Couche d'accès aux données.
     * @param economyManager    Gestionnaire d'économie Vault.
     * @param worldGuardManager Gestionnaire WorldGuard.
     */
    public GuildManager(Main plugin, GuildRepository repository, EconomyManager economyManager, WorldGuardManager worldGuardManager) {
        this.plugin = plugin;
        this.repository = repository;
        this.economyManager = economyManager;
        this.worldGuardManager = worldGuardManager;
        this.priceCalculator = new ChunkPriceCalculator();
        this.inviteManager = new InviteManager();
        this.messageManager = plugin.getMessageManager();
    }

    /**
     * Charge toutes les données depuis la base de données SQLite en mémoire.
     */
    public void loadData() {
        guildsById.clear();
        guildsByName.clear();
        playerGuildMap.clear();
        chunkGuildMap.clear();

        Collection<Guild> loadedGuilds = repository.loadAllGuilds().values();
        int totalMembers = 0;
        int totalClaims = 0;

        for (Guild guild : loadedGuilds) {
            indexGuild(guild);
            totalMembers += guild.getMemberCount();
            totalClaims += guild.getClaimCount();
        }

        plugin.getLogger().info("Charges " + loadedGuilds.size() + " guildes (" + totalMembers + " membres, " + totalClaims + " claims).");

        // Synchronisation WorldGuard globale
        if (worldGuardManager != null && worldGuardManager.isEnabled()) {
            worldGuardManager.syncAllGuilds(new ArrayList<>(loadedGuilds));
        }
    }

    /**
     * Arrêt propre du service métier.
     */
    public void shutdown() {
        guildsById.clear();
        guildsByName.clear();
        playerGuildMap.clear();
        chunkGuildMap.clear();
    }

    // ==========================================
    // NOTIFICATIONS & LOGS DE GUILDE
    // ==========================================

    /**
     * Diffuse un message à tous les membres connectés d'une guilde.
     *
     * @param guild        La guilde cible.
     * @param messageKey   La clé du message dans messages.yml.
     * @param placeholders Paires clé/valeur pour le remplacement.
     */
    public void broadcastToGuild(Guild guild, String messageKey, String... placeholders) {
        if (guild == null) return;
        String formatted = messageManager.get(messageKey, placeholders);
        for (GuildMember member : guild.getMembers()) {
            if (member.getOfflinePlayer() != null && member.getOfflinePlayer().isOnline()) {
                Player onlinePlayer = member.getOfflinePlayer().getPlayer();
                if (onlinePlayer != null) {
                    TextUtil.sendMessage(onlinePlayer, formatted);
                }
            }
        }
    }

    /**
     * Enregistre un événement dans l'historique de la guilde.
     *
     * @param guild      La guilde concernée.
     * @param actor      Le joueur initiateur (peut être null).
     * @param actionType Le type d'action.
     * @param details    Description textuelle.
     */
    public void logAction(Guild guild, Player actor, String actionType, String details) {
        if (guild == null) return;
        UUID actorUuid = actor != null ? actor.getUniqueId() : null;
        String actorName = actor != null ? actor.getName() : "Système";
        GuildLog log = new GuildLog(0, guild.getId(), actorUuid, actorName, actionType, details, System.currentTimeMillis());
        repository.saveLogAsync(log);
    }

    // ==========================================
    // CYCLE DE VIE D'UNE GUILDE
    // ==========================================

    /**
     * Crée une nouvelle guilde.
     */
    public GuildResult createGuild(Player founder, String name, String tag, double creationCost) {
        if (founder == null || name == null || tag == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        UUID founderUuid = founder.getUniqueId();

        if (isInGuild(founderUuid)) {
            return GuildResult.failure("Vous appartenez déjà à une guilde.");
        }

        if (getGuildByName(name) != null) {
            return GuildResult.failure("Une guilde portant ce nom existe déjà.");
        }

        Location loc = founder.getLocation();
        Chunk chunk = loc.getChunk();
        int chunkX = chunk.getX();
        int chunkZ = chunk.getZ();
        String worldName = loc.getWorld().getName();

        if (isChunkClaimed(worldName, chunkX, chunkZ)) {
            return GuildResult.failure("Le chunk où vous vous trouvez est déjà revendiqué par une autre guilde.");
        }

        if (creationCost > 0) {
            if (!economyManager.has(founder, creationCost)) {
                return GuildResult.failure("Vous n'avez pas les fonds nécessaires (" + economyManager.format(creationCost) + ") pour créer une guilde.");
            }
            if (!economyManager.withdraw(founder, creationCost)) {
                return GuildResult.failure("Échec de la transaction économique.");
            }
        }

        UUID guildId = UUID.randomUUID();
        long now = System.currentTimeMillis();
        Guild guild = new Guild(guildId, name, tag.toUpperCase(), "Guilde fondée par " + founder.getName(), 0.0, now);
        guild.setHome(loc);

        // Rangs initiaux
        GuildRank leaderRank = new GuildRank("leader", guildId, "Chef", 100, false, EnumSet.allOf(GuildPermission.class));
        GuildRank officerRank = new GuildRank("officer", guildId, "Officier", 50, false, EnumSet.of(
                GuildPermission.CLAIM, GuildPermission.UNCLAIM, GuildPermission.INVITE,
                GuildPermission.KICK, GuildPermission.SET_HOME, GuildPermission.HOME,
                GuildPermission.BANK_DEPOSIT, GuildPermission.BANK_WITHDRAW,
                GuildPermission.BUILD, GuildPermission.BREAK, GuildPermission.INTERACT, GuildPermission.CONTAINER
        ));
        GuildRank memberRank = new GuildRank("member", guildId, "Membre", 1, true, EnumSet.of(
                GuildPermission.HOME, GuildPermission.BANK_DEPOSIT,
                GuildPermission.BUILD, GuildPermission.BREAK, GuildPermission.INTERACT, GuildPermission.CONTAINER
        ));

        guild.addRank(leaderRank);
        guild.addRank(officerRank);
        guild.addRank(memberRank);

        GuildMember founderMember = new GuildMember(founderUuid, guildId, "leader", now, now, 0.0);
        guild.addMember(founderMember);

        GuildClaim initialClaim = new GuildClaim(0, guildId, worldName, chunkX, chunkZ, now);
        guild.addClaim(initialClaim);

        indexGuild(guild);

        repository.saveGuildAsync(guild);
        repository.saveRankAsync(leaderRank);
        repository.saveRankAsync(officerRank);
        repository.saveRankAsync(memberRank);
        repository.saveMemberAsync(founderMember);
        repository.saveClaimAsync(initialClaim);

        if (worldGuardManager != null) {
            worldGuardManager.createOrUpdateRegion(guild, loc.getWorld(), chunkX, chunkZ);
        }

        logAction(guild, founder, "CREATION", "Création de la guilde avec le chunk initial (" + chunkX + ", " + chunkZ + ")");

        return GuildResult.success("La guilde " + guild.getFormattedName() + " a été créée avec succès !");
    }

    /**
     * Dissout définitivement une guilde existante.
     */
    public GuildResult disbandGuild(Player actor, Guild guild) {
        if (actor == null || guild == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        GuildMember actorMember = guild.getMember(actor.getUniqueId());
        if (actorMember == null) {
            return GuildResult.failure("Vous ne faites pas partie de cette guilde.");
        }

        GuildRank actorRank = guild.getRank(actorMember.getRankId());
        GuildRank leaderRank = guild.getLeaderRank();
        if (actorRank == null || leaderRank == null || !actorRank.equals(leaderRank)) {
            return GuildResult.failure("Seul le Chef de la guilde peut dissoudre la guilde.");
        }

        logAction(guild, actor, "DISBAND", "Dissolution de la guilde par " + actor.getName());

        if (worldGuardManager != null) {
            worldGuardManager.removeGuildRegions(guild);
        }

        unindexGuild(guild);
        repository.deleteGuildAsync(guild.getId());

        return GuildResult.success("La guilde <white>" + guild.getName() + "</white> a été dissoute.");
    }

    // ==========================================
    // GESTION DES MEMBRES & RANGS
    // ==========================================

    /**
     * Intègre un joueur dans une guilde.
     */
    public GuildResult addMember(Guild guild, Player player) {
        if (guild == null || player == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        if (isInGuild(player.getUniqueId())) {
            return GuildResult.failure("Le joueur appartient déjà à une guilde.");
        }

        GuildRank defaultRank = guild.getDefaultRank();
        if (defaultRank == null) {
            return GuildResult.failure("La guilde ne possède aucun rang par défaut.");
        }

        GuildMember member = new GuildMember(player.getUniqueId(), guild.getId(), defaultRank.getId(), System.currentTimeMillis(), System.currentTimeMillis(), 0.0);
        guild.addMember(member);
        playerGuildMap.put(player.getUniqueId(), guild.getId());
        repository.saveMemberAsync(member);

        if (worldGuardManager != null) {
            worldGuardManager.syncGuildMembers(guild);
        }

        logAction(guild, player, "JOIN", player.getName() + " a rejoint la guilde (" + defaultRank.getName() + ")");
        broadcastToGuild(guild, "guild.broadcast.member_joined", "{player}", player.getName());

        return GuildResult.success("Le joueur <white>" + player.getName() + "</white> a rejoint la guilde !");
    }

    /**
     * Expulse un membre d'une guilde.
     */
    public GuildResult kickMember(Player actor, Guild guild, UUID targetUuid) {
        if (actor == null || guild == null || targetUuid == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        if (actor.getUniqueId().equals(targetUuid)) {
            return GuildResult.failure("Vous ne pouvez pas vous expulser vous-même. Utilisez /guild leave.");
        }

        GuildRank actorRank = guild.getMemberRank(actor.getUniqueId());
        if (actorRank == null || !actorRank.hasPermission(GuildPermission.KICK)) {
            return GuildResult.failure("Vous n'avez pas la permission d'expulser des membres.");
        }

        GuildMember targetMember = guild.getMember(targetUuid);
        if (targetMember == null) {
            return GuildResult.failure("Ce joueur n'est pas membre de la guilde.");
        }

        GuildRank targetRank = guild.getRank(targetMember.getRankId());
        if (targetRank != null && targetRank.getPriority() >= actorRank.getPriority()) {
            return GuildResult.failure("Vous ne pouvez pas expulser un membre ayant un rang supérieur ou égal au vôtre.");
        }

        String targetName = targetMember.getOfflinePlayer() != null && targetMember.getOfflinePlayer().getName() != null
                ? targetMember.getOfflinePlayer().getName()
                : targetUuid.toString().substring(0, 8);

        guild.removeMember(targetUuid);
        playerGuildMap.remove(targetUuid);
        repository.deleteMemberAsync(targetUuid);

        if (worldGuardManager != null) {
            worldGuardManager.syncGuildMembers(guild);
        }

        logAction(guild, actor, "KICK", targetName + " a été expulsé par " + actor.getName());
        broadcastToGuild(guild, "guild.broadcast.member_kicked", "{player}", targetName, "{actor}", actor.getName());

        return GuildResult.success("Le membre a été expulsé de la guilde.");
    }

    /**
     * Permet à un membre de quitter volontairement sa guilde.
     */
    public GuildResult leaveGuild(Player player, Guild guild) {
        if (player == null || guild == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        GuildMember member = guild.getMember(player.getUniqueId());
        if (member == null) {
            return GuildResult.failure("Vous n'êtes pas membre de cette guilde.");
        }

        GuildRank leaderRank = guild.getLeaderRank();
        if (leaderRank != null && member.getRankId().equalsIgnoreCase(leaderRank.getId())) {
            return GuildResult.failure("En tant que Chef, vous ne pouvez pas quitter la guilde. Transférez le lead ou dissolvez la guilde.");
        }

        guild.removeMember(player.getUniqueId());
        playerGuildMap.remove(player.getUniqueId());
        repository.deleteMemberAsync(player.getUniqueId());

        if (worldGuardManager != null) {
            worldGuardManager.syncGuildMembers(guild);
        }

        logAction(guild, player, "LEAVE", player.getName() + " a quitté la guilde.");
        broadcastToGuild(guild, "guild.broadcast.member_left", "{player}", player.getName());

        return GuildResult.success("Vous avez quitté la guilde.");
    }

    /**
     * Modifie le rang d'un membre.
     */
    public GuildResult setMemberRank(Player actor, Guild guild, UUID targetUuid, String newRankId) {
        if (actor == null || guild == null || targetUuid == null || newRankId == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        GuildRank actorRank = guild.getMemberRank(actor.getUniqueId());
        if (actorRank == null) {
            return GuildResult.failure("Vous ne faites pas partie de cette guilde.");
        }

        GuildMember targetMember = guild.getMember(targetUuid);
        if (targetMember == null) {
            return GuildResult.failure("Le joueur cible n'est pas dans la guilde.");
        }

        GuildRank newRank = guild.getRank(newRankId);
        if (newRank == null) {
            return GuildResult.failure("Le rang spécifié n'existe pas.");
        }

        GuildRank targetCurrentRank = guild.getRank(targetMember.getRankId());
        boolean isPromote = targetCurrentRank == null || newRank.getPriority() > targetCurrentRank.getPriority();

        if (isPromote && !actorRank.hasPermission(GuildPermission.PROMOTE)) {
            return GuildResult.failure("Vous n'avez pas la permission de promouvoir des membres.");
        }
        if (!isPromote && !actorRank.hasPermission(GuildPermission.DEMOTE)) {
            return GuildResult.failure("Vous n'avez pas la permission de rétrograder des membres.");
        }

        GuildRank leaderRank = guild.getLeaderRank();
        boolean isLeader = actorRank.equals(leaderRank);
        if (!isLeader && newRank.getPriority() >= actorRank.getPriority()) {
            return GuildResult.failure("Vous ne pouvez pas attribuer un rang supérieur ou égal au vôtre.");
        }

        targetMember.setRankId(newRank.getId());
        repository.saveMemberAsync(targetMember);

        if (worldGuardManager != null) {
            worldGuardManager.syncGuildMembers(guild);
        }

        String targetName = targetMember.getOfflinePlayer() != null && targetMember.getOfflinePlayer().getName() != null
                ? targetMember.getOfflinePlayer().getName()
                : targetUuid.toString().substring(0, 8);

        logAction(guild, actor, "RANK_CHANGE", targetName + " assigné au rang " + newRank.getName());
        broadcastToGuild(guild, "guild.broadcast.rank_changed", "{player}", targetName, "{rank}", newRank.getName(), "{actor}", actor.getName());

        return GuildResult.success("Le rang du membre a été mis à jour vers <white>" + newRank.getName() + "</white>.");
    }

    /**
     * Crée un nouveau rang personnalisé pour la guilde.
     */
    public GuildResult createRank(Player actor, Guild guild, String rankId, String name, int priority) {
        if (actor == null || guild == null || rankId == null || name == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        GuildRank actorRank = guild.getMemberRank(actor.getUniqueId());
        if (actorRank == null || !actorRank.hasPermission(GuildPermission.MANAGE_RANKS)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à gérer les rangs.");
        }

        GuildRank leaderRank = guild.getLeaderRank();
        boolean isLeader = actorRank.equals(leaderRank);
        if (!isLeader && priority >= actorRank.getPriority()) {
            return GuildResult.failure("Vous ne pouvez pas créer un rang ayant une priorité supérieure ou égale à la vôtre.");
        }

        String cleanId = rankId.toLowerCase();
        if (guild.getRank(cleanId) != null) {
            return GuildResult.failure("Un rang avec cet identifiant existe déjà.");
        }

        GuildRank newRank = new GuildRank(cleanId, guild.getId(), name, priority, false, EnumSet.of(GuildPermission.HOME, GuildPermission.BANK_DEPOSIT, GuildPermission.BUILD, GuildPermission.BREAK, GuildPermission.INTERACT, GuildPermission.CONTAINER));
        guild.addRank(newRank);
        repository.saveRankAsync(newRank);

        logAction(guild, actor, "RANK_CREATE", "Création du rang " + name + " (Priorité: " + priority + ")");

        return GuildResult.success("Le rang <yellow>" + name + "</yellow> (ID: " + cleanId + ", Priorité: " + priority + ") a été créé avec succès !");
    }

    /**
     * Supprime un rang existant de la guilde.
     */
    public GuildResult deleteRank(Player actor, Guild guild, String rankId) {
        if (actor == null || guild == null || rankId == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        GuildRank actorRank = guild.getMemberRank(actor.getUniqueId());
        if (actorRank == null || !actorRank.hasPermission(GuildPermission.MANAGE_RANKS)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à gérer les rangs.");
        }

        GuildRank rank = guild.getRank(rankId.toLowerCase());
        if (rank == null) {
            return GuildResult.failure("Le rang spécifié n'existe pas.");
        }

        GuildRank leaderRank = guild.getLeaderRank();
        GuildRank defaultRank = guild.getDefaultRank();

        if (rank.equals(leaderRank)) {
            return GuildResult.failure("Vous ne pouvez pas supprimer le rang de Chef.");
        }
        if (rank.equals(defaultRank)) {
            return GuildResult.failure("Vous ne pouvez pas supprimer le rang par défaut.");
        }

        GuildRank fallbackRank = defaultRank != null ? defaultRank : guild.getRanks().stream().filter(r -> !r.equals(rank)).findFirst().orElse(null);
        if (fallbackRank == null) {
            return GuildResult.failure("Impossible de supprimer ce rang sans rang de repli.");
        }

        // Migrer les membres occupant ce rang
        for (GuildMember m : guild.getMembers()) {
            if (m.getRankId().equalsIgnoreCase(rank.getId())) {
                m.setRankId(fallbackRank.getId());
                repository.saveMemberAsync(m);
            }
        }

        guild.removeRank(rank.getId());
        repository.deleteRankAsync(rank.getId(), guild.getId());

        logAction(guild, actor, "RANK_DELETE", "Suppression du rang " + rank.getName());

        return GuildResult.success("Le rang <white>" + rank.getName() + "</white> a été supprimé.");
    }

    /**
     * Active ou désactive une permission spécifique pour un rang.
     */
    public GuildResult setRankPermission(Player actor, Guild guild, String rankId, GuildPermission permission, boolean state) {
        if (actor == null || guild == null || rankId == null || permission == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        GuildRank actorRank = guild.getMemberRank(actor.getUniqueId());
        if (actorRank == null || !actorRank.hasPermission(GuildPermission.MANAGE_RANKS)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à gérer les rangs.");
        }

        GuildRank rank = guild.getRank(rankId.toLowerCase());
        if (rank == null) {
            return GuildResult.failure("Le rang spécifié n'existe pas.");
        }

        GuildRank leaderRank = guild.getLeaderRank();
        if (rank.equals(leaderRank)) {
            return GuildResult.failure("Vous ne pouvez pas modifier les permissions du Chef.");
        }

        if (state) {
            rank.addPermission(permission);
        } else {
            rank.removePermission(permission);
        }

        repository.saveRankAsync(rank);
        logAction(guild, actor, "RANK_PERM", "Permission " + permission.name() + " -> " + state + " pour " + rank.getName());

        return GuildResult.success("Permission <yellow>" + permission.name() + "</yellow> mise à <aqua>" + state + "</aqua> pour le rang <yellow>" + rank.getName() + "</yellow>.");
    }

    // ==========================================
    // PERSONNALISATION (COULEUR & TAG)
    // ==========================================

    public GuildResult setGuildColor(Player actor, Guild guild, String hexColor) {
        if (actor == null || guild == null || hexColor == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        if (!guild.hasPermission(actor.getUniqueId(), GuildPermission.MANAGE_GUILD)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à modifier les paramètres de la guilde.");
        }

        if (!hexColor.matches("^#[0-9a-fA-F]{6}$")) {
            return GuildResult.failure("Format de couleur invalide. Utilisez un code hexadécimal valide à 6 caractères (ex: #00FFAA ou #FF5555).");
        }

        guild.setColor(hexColor);
        repository.saveGuildAsync(guild);
        logAction(guild, actor, "COLOR", "Couleur modifiée vers " + hexColor);

        return GuildResult.success("La couleur de la guilde a été changée pour <color:" + hexColor + ">" + hexColor + "</color> !");
    }

    public GuildResult setGuildTag(Player actor, Guild guild, String newTag) {
        if (actor == null || guild == null || newTag == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        if (!guild.hasPermission(actor.getUniqueId(), GuildPermission.MANAGE_GUILD)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à modifier les paramètres de la guilde.");
        }

        if (!newTag.matches("^[a-zA-Z0-9_]{2,6}$")) {
            return GuildResult.failure("Le tag doit comporter entre 2 et 6 caractères alphanumériques.");
        }

        guild.setTag(newTag.toUpperCase());
        repository.saveGuildAsync(guild);
        logAction(guild, actor, "TAG", "Tag modifié vers [" + newTag.toUpperCase() + "]");

        return GuildResult.success("Le tag de la guilde a été changé pour " + guild.getFormattedTag() + " !");
    }

    // ==========================================
    // GESTION DES CLAIMS (TERRITOIRES)
    // ==========================================

    public GuildResult claimChunk(Player player, Guild guild, World world, int chunkX, int chunkZ) {
        if (player == null || guild == null || world == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        String worldName = world.getName();

        if (!guild.hasPermission(player.getUniqueId(), GuildPermission.CLAIM)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à revendiquer de nouveaux territoires.");
        }

        if (isChunkClaimed(worldName, chunkX, chunkZ)) {
            return GuildResult.failure("Ce chunk est déjà revendiqué.");
        }

        double price = priceCalculator.calculateNextChunkPrice(guild);

        if (!guild.hasBalance(price)) {
            return GuildResult.failure("La banque de guilde n'a pas assez d'argent (Requis: " + economyManager.format(price) + ", Solde: " + economyManager.format(guild.getBalance()) + ").");
        }

        guild.withdraw(price);
        GuildClaim claim = new GuildClaim(0, guild.getId(), worldName, chunkX, chunkZ, System.currentTimeMillis());
        guild.addClaim(claim);
        chunkGuildMap.put(getChunkKey(worldName, chunkX, chunkZ), guild.getId());

        repository.saveGuildAsync(guild);
        repository.saveClaimAsync(claim);

        if (worldGuardManager != null) {
            worldGuardManager.createOrUpdateRegion(guild, world, chunkX, chunkZ);
        }

        logAction(guild, player, "CLAIM", "Claim (" + chunkX + ", " + chunkZ + ") pour " + economyManager.format(price));
        broadcastToGuild(guild, "guild.broadcast.chunk_claimed", "{player}", player.getName(), "{chunk_x}", String.valueOf(chunkX), "{chunk_z}", String.valueOf(chunkZ), "{cost}", economyManager.format(price));

        return GuildResult.success("Chunk revendiqué avec succès pour <yellow>" + economyManager.format(price) + "</yellow> ! (Total: " + guild.getClaimCount() + " chunks)");
    }

    public GuildResult unclaimChunk(Player player, Guild guild, World world, int chunkX, int chunkZ) {
        if (player == null || guild == null || world == null) {
            return GuildResult.failure("Paramètres invalides.");
        }

        String worldName = world.getName();

        if (!guild.hasPermission(player.getUniqueId(), GuildPermission.UNCLAIM)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à abandonner des territoires.");
        }

        if (!guild.isClaimed(worldName, chunkX, chunkZ)) {
            return GuildResult.failure("Ce chunk n'appartient pas à votre guilde.");
        }

        if (guild.getClaimCount() <= 1) {
            return GuildResult.failure("Vous ne pouvez pas abandonner le dernier chunk de votre guilde.");
        }

        if (worldGuardManager != null) {
            worldGuardManager.removeRegion(guild, world, chunkX, chunkZ);
        }

        guild.removeClaim(worldName, chunkX, chunkZ);
        chunkGuildMap.remove(getChunkKey(worldName, chunkX, chunkZ));
        repository.deleteClaimAsync(worldName, chunkX, chunkZ);

        logAction(guild, player, "UNCLAIM", "Abandon du chunk (" + chunkX + ", " + chunkZ + ")");
        broadcastToGuild(guild, "guild.broadcast.chunk_unclaimed", "{player}", player.getName(), "{chunk_x}", String.valueOf(chunkX), "{chunk_z}", String.valueOf(chunkZ));

        return GuildResult.success("Le chunk a été libéré.");
    }

    // ==========================================
    // GESTION DE LA BANQUE
    // ==========================================

    public GuildResult depositMoney(Player player, Guild guild, double amount) {
        if (player == null || guild == null || amount <= 0) {
            return GuildResult.failure("Montant invalide.");
        }

        if (!guild.hasPermission(player.getUniqueId(), GuildPermission.BANK_DEPOSIT)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à déposer dans la banque.");
        }

        if (!economyManager.has(player, amount)) {
            return GuildResult.failure("Vous n'avez pas assez d'argent sur votre compte personnel.");
        }

        boolean withdrawn = economyManager.withdraw(player, amount);
        if (!withdrawn) {
            return GuildResult.failure("Échec de la transaction bancaire.");
        }

        guild.deposit(amount);
        GuildMember member = guild.getMember(player.getUniqueId());
        if (member != null) {
            member.addContribution(amount);
            repository.saveMemberAsync(member);
        }
        repository.saveGuildAsync(guild);

        logAction(guild, player, "DEPOSIT", player.getName() + " a déposé " + economyManager.format(amount));
        broadcastToGuild(guild, "guild.broadcast.bank_deposit", "{player}", player.getName(), "{amount}", economyManager.format(amount), "{balance}", economyManager.format(guild.getBalance()));

        return GuildResult.success("Vous avez déposé <green>" + economyManager.format(amount) + "</green> dans la banque de guilde ! (Nouveau solde: " + economyManager.format(guild.getBalance()) + ")");
    }

    public GuildResult withdrawMoney(Player player, Guild guild, double amount) {
        if (player == null || guild == null || amount <= 0) {
            return GuildResult.failure("Montant invalide.");
        }

        if (!guild.hasPermission(player.getUniqueId(), GuildPermission.BANK_WITHDRAW)) {
            return GuildResult.failure("Votre rang ne vous autorise pas à retirer de l'argent de la banque.");
        }

        if (!guild.hasBalance(amount)) {
            return GuildResult.failure("Le solde de la banque de guilde est insuffisant (Solde actuel: " + economyManager.format(guild.getBalance()) + ").");
        }

        boolean withdrawnFromGuild = guild.withdraw(amount);
        if (!withdrawnFromGuild) {
            return GuildResult.failure("Impossible de débiter la banque de guilde.");
        }

        boolean depositedToPlayer = economyManager.deposit(player, amount);
        if (!depositedToPlayer) {
            guild.deposit(amount);
            return GuildResult.failure("Échec du crédit sur votre compte personnel.");
        }

        repository.saveGuildAsync(guild);
        logAction(guild, player, "WITHDRAW", player.getName() + " a retiré " + economyManager.format(amount));
        broadcastToGuild(guild, "guild.broadcast.bank_withdraw", "{player}", player.getName(), "{amount}", economyManager.format(amount), "{balance}", economyManager.format(guild.getBalance()));

        return GuildResult.success("Vous avez retiré <green>" + economyManager.format(amount) + "</green> de la banque de guilde.");
    }

    // ==========================================
    // GETTERS & INDEXATION
    // ==========================================

    public Guild getGuildById(UUID guildId) {
        if (guildId == null) return null;
        return guildsById.get(guildId);
    }

    public Guild getGuildByName(String name) {
        if (name == null) return null;
        UUID id = guildsByName.get(name.toLowerCase());
        return id != null ? guildsById.get(id) : null;
    }

    public Guild getGuildByPlayer(UUID playerUuid) {
        if (playerUuid == null) return null;
        UUID guildId = playerGuildMap.get(playerUuid);
        return guildId != null ? guildsById.get(guildId) : null;
    }

    public boolean isInGuild(UUID playerUuid) {
        return playerGuildMap.containsKey(playerUuid);
    }

    public Guild getGuildAt(String world, int chunkX, int chunkZ) {
        UUID guildId = chunkGuildMap.get(getChunkKey(world, chunkX, chunkZ));
        return guildId != null ? guildsById.get(guildId) : null;
    }

    public boolean isChunkClaimed(String world, int chunkX, int chunkZ) {
        return chunkGuildMap.containsKey(getChunkKey(world, chunkX, chunkZ));
    }

    public Collection<Guild> getAllGuilds() {
        return Collections.unmodifiableCollection(guildsById.values());
    }

    public ChunkPriceCalculator getPriceCalculator() {
        return priceCalculator;
    }

    public EconomyManager getEconomyManager() {
        return economyManager;
    }

    public InviteManager getInviteManager() {
        return inviteManager;
    }

    public WorldGuardManager getWorldGuardManager() {
        return worldGuardManager;
    }

    private void indexGuild(Guild guild) {
        guildsById.put(guild.getId(), guild);
        guildsByName.put(guild.getName().toLowerCase(), guild.getId());
        for (GuildMember member : guild.getMembers()) {
            playerGuildMap.put(member.getPlayerUuid(), guild.getId());
        }
        for (GuildClaim claim : guild.getClaims()) {
            chunkGuildMap.put(getChunkKey(claim.getWorldName(), claim.getChunkX(), claim.getChunkZ()), guild.getId());
        }
    }

    private void unindexGuild(Guild guild) {
        guildsById.remove(guild.getId());
        guildsByName.remove(guild.getName().toLowerCase());
        for (GuildMember member : guild.getMembers()) {
            playerGuildMap.remove(member.getPlayerUuid());
        }
        for (GuildClaim claim : guild.getClaims()) {
            chunkGuildMap.remove(getChunkKey(claim.getWorldName(), claim.getChunkX(), claim.getChunkZ()));
        }
    }

    private String getChunkKey(String world, int x, int z) {
        return (world != null ? world.toLowerCase() : "world") + ":" + x + "," + z;
    }
}
