package com.tiedup.remake.bounty;

import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.SystemMessageManager;
import com.tiedup.remake.core.TiedUpMod;
import java.util.*;
import javax.annotation.Nullable;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.saveddata.SavedData;

/**
 * World-saved data manager for bounties.
 *
 *
 * Manages all active bounties, handles expiration, delivery rewards,
 * and stores bounties for offline players.
 */
public class BountyManager extends SavedData {

    private static final String DATA_NAME = "tiedup_bounties";

    /** Pending rewards expire after 30 real days (in milliseconds). */
    private static final long PENDING_REWARD_EXPIRATION_MS =
        30L * 24 * 60 * 60 * 1000;

    // Active bounties
    private final List<Bounty> bounties = new ArrayList<>();

    // Bounties for offline players (to return reward when they log in)
    // Stored with timestamp via Bounty.creationTime + durationSeconds
    private final List<Bounty> pendingRewards = new ArrayList<>();

    // ==================== CONSTRUCTION ====================

    public BountyManager() {}

    public static BountyManager create() {
        return new BountyManager();
    }

    public static BountyManager load(CompoundTag tag) {
        BountyManager manager = new BountyManager();

        // Load active bounties
        ListTag bountiesTag = tag.getList("bounties", Tag.TAG_COMPOUND);
        for (int i = 0; i < bountiesTag.size(); i++) {
            manager.bounties.add(Bounty.load(bountiesTag.getCompound(i)));
        }

        // Load pending rewards (with expiration cleanup)
        ListTag pendingTag = tag.getList("pendingRewards", Tag.TAG_COMPOUND);
        int expiredCount = 0;
        for (int i = 0; i < pendingTag.size(); i++) {
            Bounty bounty = Bounty.load(pendingTag.getCompound(i));
            if (!isPendingRewardExpired(bounty)) {
                manager.pendingRewards.add(bounty);
            } else {
                expiredCount++;
            }
        }

        if (expiredCount > 0) {
            TiedUpMod.LOGGER.info(
                "[BOUNTY] Cleaned up {} expired pending rewards (>30 days)",
                expiredCount
            );
        }

        TiedUpMod.LOGGER.info(
            "[BOUNTY] Loaded {} active bounties, {} pending rewards",
            manager.bounties.size(),
            manager.pendingRewards.size()
        );

        return manager;
    }

    @Override
    public CompoundTag save(CompoundTag tag) {
        // Save active bounties
        ListTag bountiesTag = new ListTag();
        for (Bounty bounty : bounties) {
            bountiesTag.add(bounty.save());
        }
        tag.put("bounties", bountiesTag);

        // Save pending rewards
        ListTag pendingTag = new ListTag();
        for (Bounty bounty : pendingRewards) {
            pendingTag.add(bounty.save());
        }
        tag.put("pendingRewards", pendingTag);

        return tag;
    }

    // ==================== ACCESS ====================

    /**
     * Get the BountyManager for a world.
     */
    public static BountyManager get(ServerLevel level) {
        return level
            .getDataStorage()
            .computeIfAbsent(
                BountyManager::load,
                BountyManager::create,
                DATA_NAME
            );
    }

    /**
     * Get the BountyManager from a server.
     */
    public static BountyManager get(MinecraftServer server) {
        ServerLevel overworld = server.overworld();
        return get(overworld);
    }

    // ==================== BOUNTY MANAGEMENT ====================

    /**
     * Get all active bounties (removes expired ones).
     */
    public List<Bounty> getBounties(ServerLevel level) {
        // Clean up expired bounties
        Iterator<Bounty> it = bounties.iterator();
        while (it.hasNext()) {
            Bounty bounty = it.next();
            if (bounty.isExpired()) {
                it.remove();
                onBountyExpired(level, bounty);
            }
        }
        setDirty();
        return new ArrayList<>(bounties);
    }

    /**
     * Add a new bounty.
     */
    public void addBounty(Bounty bounty) {
        bounties.add(bounty);
        setDirty();
        TiedUpMod.LOGGER.info(
            "[BOUNTY] New bounty: {} on {} by {}",
            bounty.getId(),
            bounty.getTargetName(),
            bounty.getClientName()
        );
    }

    /**
     * Get a bounty by ID.
     */
    @Nullable
    public Bounty getBountyById(String id) {
        for (Bounty bounty : bounties) {
            if (bounty.getId().equals(id)) {
                return bounty;
            }
        }
        return null;
    }

    /**
     * Cancel a bounty.
     * Only the client or an admin can cancel.
     * If client cancels, they get their reward back.
     */
    public boolean cancelBounty(ServerPlayer player, String bountyId) {
        Bounty bounty = getBountyById(bountyId);
        if (bounty == null) {
            return false;
        }

        boolean isAdmin = player.hasPermissions(2);
        boolean isClient = bounty.isClient(player.getUUID());

        if (!isClient && !isAdmin) {
            return false;
        }

        bounties.remove(bounty);
        setDirty();

        // Return reward to client (or drop if admin cancelled)
        if (isClient) {
            giveReward(player, bounty);
            broadcastMessage(
                player.server,
                player.getName().getString() +
                    " cancelled their bounty on " +
                    bounty.getTargetName()
            );
        } else {
            onBountyExpired(player.serverLevel(), bounty);
            broadcastMessage(
                player.server,
                player.getName().getString() +
                    " (admin) cancelled bounty on " +
                    bounty.getTargetName()
            );
        }

        return true;
    }

    // ==================== DELIVERY ====================

    /**
     * Try to deliver a captive to a client.
     * Called when a hunter brings a captive near the bounty client.
     *
     * @param hunter The player delivering the captive
     * @param client The bounty client receiving the captive
     * @param target The captive being delivered
     * @return true if bounty was fulfilled
     */
    public boolean tryDeliverCaptive(
        ServerPlayer hunter,
        ServerPlayer client,
        ServerPlayer target
    ) {
        boolean delivered = false;

        Iterator<Bounty> it = bounties.iterator();
        while (it.hasNext()) {
            Bounty bounty = it.next();

            // Skip expired
            if (bounty.isExpired()) {
                continue;
            }

            // Check if this bounty matches
            if (bounty.matches(client.getUUID(), target.getUUID())) {
                it.remove();
                setDirty();

                // Give reward to hunter
                giveReward(hunter, bounty);
                delivered = true;

                broadcastMessage(
                    hunter.server,
                    hunter.getName().getString() +
                        " delivered " +
                        target.getName().getString() +
                        " to " +
                        client.getName().getString() +
                        " for " +
                        bounty.getRewardDescription() +
                        "!"
                );

                TiedUpMod.LOGGER.info(
                    "[BOUNTY] Delivered: {} brought {} to {}",
                    hunter.getName().getString(),
                    target.getName().getString(),
                    client.getName().getString()
                );
            }
        }

        return delivered;
    }

    // ==================== EXPIRATION ====================

    /**
     * Handle bounty expiration.
     * Returns reward to client if online, otherwise stores for later.
     */
    private void onBountyExpired(ServerLevel level, Bounty bounty) {
        ServerPlayer client = level
            .getServer()
            .getPlayerList()
            .getPlayer(bounty.getClientId());

        if (client != null) {
            // Client is online - return reward
            giveReward(client, bounty);
            SystemMessageManager.sendChatToPlayer(
                client,
                "Your bounty on " +
                    bounty.getTargetName() +
                    " has expired. Reward returned.",
                ChatFormatting.YELLOW
            );
        } else {
            // Client is offline - store for later
            pendingRewards.add(bounty);
            setDirty();
        }

        TiedUpMod.LOGGER.info(
            "[BOUNTY] Expired: {} on {}",
            bounty.getClientName(),
            bounty.getTargetName()
        );
    }

    /**
     * Check for pending rewards when a player joins.
     */
    public void onPlayerJoin(ServerPlayer player) {
        Iterator<Bounty> it = pendingRewards.iterator();
        while (it.hasNext()) {
            Bounty bounty = it.next();
            if (bounty.isClient(player.getUUID())) {
                giveReward(player, bounty);
                it.remove();
                setDirty();

                SystemMessageManager.sendChatToPlayer(
                    player,
                    "Your expired bounty reward has been returned: " +
                        bounty.getRewardDescription(),
                    ChatFormatting.YELLOW
                );
            }
        }
    }

    // ==================== VALIDATION ====================

    /**
     * Check if a player can create a new bounty.
     */
    public boolean canCreateBounty(ServerPlayer player, ServerLevel level) {
        if (player.hasPermissions(2)) {
            return true; // Admins bypass limit
        }

        int count = 0;
        for (Bounty bounty : bounties) {
            if (bounty.isClient(player.getUUID())) {
                count++;
            }
        }

        int max = SettingsAccessor.getMaxBounties(level.getGameRules());
        return count < max;
    }

    /**
     * Get the number of active bounties for a player.
     */
    public int getBountyCount(UUID playerId) {
        int count = 0;
        for (Bounty bounty : bounties) {
            if (bounty.isClient(playerId)) {
                count++;
            }
        }
        return count;
    }

    // ==================== HELPERS ====================

    private void giveReward(ServerPlayer player, Bounty bounty) {
        ItemStack reward = bounty.getReward();
        if (!reward.isEmpty()) {
            if (!player.getInventory().add(reward)) {
                // Inventory full - drop at feet
                player.drop(reward, false);
            }
        }
    }

    private void broadcastMessage(MinecraftServer server, String message) {
        server
            .getPlayerList()
            .broadcastSystemMessage(
                Component.translatable("msg.tiedup.bounty.broadcast", message)
                    .withStyle(ChatFormatting.GOLD),
                false
            );
    }

    /**
     * Check if a pending reward has been waiting too long (>30 days).
     * Uses the bounty's original expiration time as baseline.
     */
    private static boolean isPendingRewardExpired(Bounty bounty) {
        // Calculate when the bounty originally expired
        // creationTime is in milliseconds, durationSeconds needs conversion
        long expirationTime =
            bounty.getCreationTime() + (bounty.getDurationSeconds() * 1000L);
        long now = System.currentTimeMillis();

        // Check if it's been more than 30 days since expiration
        return (now - expirationTime) > PENDING_REWARD_EXPIRATION_MS;
    }
}
