package com.tiedup.remake.labor;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.Nullable;

/**
 * Unified "Bring X Items" labor task.
 *
 * DESIGN CHANGE (v2.0):
 * All tasks are now simplified to "bring X items" instead of:
 * - "Mine X blocks" (event-based tracking)
 * - "Kill X mobs" (event-based tracking)
 * - "Chop X logs" (event-based tracking)
 * - "Collect X items" (inventory-based tracking)
 *
 * Benefits:
 * - Single task class (was 4)
 * - Periodic inventory checks (no event dependencies)
 * - Works with ANY item source (/give, crafting, trading, mining, looting)
 * - Maid collects items and deposits in cell chest
 * - Tools are provided and automatically retrieved
 */
public class LaborTask {

    private final UUID id;
    private final Item targetItem; // What to bring
    private final int quota; // How many
    private final int value; // Emerald reward
    private final ItemStack toolGiven; // Tool provided (can be null)
    private int progress; // Current count (based on inventory)
    private UUID campId;

    // ==================== CONSTRUCTORS ====================

    public LaborTask(
        Item targetItem,
        int quota,
        int value,
        @Nullable ItemStack tool
    ) {
        this.id = UUID.randomUUID();
        this.targetItem = targetItem;
        this.quota = quota;
        this.value = value;
        this.toolGiven = tool != null ? tagAsLaborTool(tool.copy()) : null;
        this.progress = 0;
    }

    // ==================== GETTERS ====================

    public UUID getId() {
        return id;
    }

    public Item getTargetItem() {
        return targetItem;
    }

    public int getQuota() {
        return quota;
    }

    public int getProgress() {
        return progress;
    }

    public int getValue() {
        return value;
    }

    public UUID getCampId() {
        return campId;
    }

    public void setCampId(UUID campId) {
        this.campId = campId;
    }

    @Nullable
    public ItemStack getToolGiven() {
        return toolGiven;
    }

    /**
     * Get the tool type (e.g., Items.IRON_AXE).
     * Used for retrieving stored tools from cell chest.
     */
    @Nullable
    public Item getToolType() {
        return toolGiven != null ? toolGiven.getItem() : null;
    }

    /**
     * Check if this is a combat task (requires killing mobs).
     * Combat tasks use a sword and depend on mob spawns (time-of-day dependent).
     */
    public boolean isCombatTask() {
        return toolGiven != null && toolGiven.getItem() == Items.IRON_SWORD;
    }

    // ==================== PROGRESS ====================

    /**
     * Check if the task is complete.
     */
    public boolean isComplete() {
        return progress >= quota;
    }

    /**
     * Get completion percentage (0-100).
     */
    public int getProgressPercent() {
        if (quota <= 0) return 100;
        return Math.min(100, (progress * 100) / quota);
    }

    /**
     * Check progress by counting matching items in worker's inventory.
     * Called periodically (every ~5 seconds) by MaidManagePrisonersGoal.
     *
     * @param worker The player doing the task
     * @param level The server level (unused but kept for API compatibility)
     * @return true if progress was made since last check
     */
    public boolean checkProgress(ServerPlayer worker, ServerLevel level) {
        int count = 0;
        var inventory = worker.getInventory();
        for (int i = 0; i < inventory.getContainerSize(); i++) {
            ItemStack stack = inventory.getItem(i);
            if (!stack.isEmpty() && isAcceptableItem(stack.getItem())) {
                count += stack.getCount();
            }
        }

        int oldProgress = progress;
        progress = Math.min(count, quota);
        return progress > oldProgress;
    }

    /**
     * Check if an item is acceptable for this task.
     * For logs, accept any type of log (oak, birch, spruce, etc.)
     */
    private boolean isAcceptableItem(Item item) {
        // Direct match
        if (item == targetItem) {
            return true;
        }

        // If target is any type of log, accept all logs
        if (isLogItem(targetItem) && isLogItem(item)) {
            return true;
        }

        // If target is any type of planks, accept all planks
        if (isPlankItem(targetItem) && isPlankItem(item)) {
            return true;
        }

        return false;
    }

    /**
     * Check if an item is a log.
     */
    private boolean isLogItem(Item item) {
        return (
            item == Items.OAK_LOG ||
            item == Items.BIRCH_LOG ||
            item == Items.SPRUCE_LOG ||
            item == Items.DARK_OAK_LOG ||
            item == Items.JUNGLE_LOG ||
            item == Items.ACACIA_LOG ||
            item == Items.MANGROVE_LOG ||
            item == Items.CHERRY_LOG ||
            item == Items.CRIMSON_STEM ||
            item == Items.WARPED_STEM
        );
    }

    /**
     * Check if an item is a plank.
     */
    private boolean isPlankItem(Item item) {
        return (
            item == Items.OAK_PLANKS ||
            item == Items.BIRCH_PLANKS ||
            item == Items.SPRUCE_PLANKS ||
            item == Items.DARK_OAK_PLANKS ||
            item == Items.JUNGLE_PLANKS ||
            item == Items.ACACIA_PLANKS ||
            item == Items.MANGROVE_PLANKS ||
            item == Items.CHERRY_PLANKS ||
            item == Items.CRIMSON_PLANKS ||
            item == Items.WARPED_PLANKS ||
            item == Items.BAMBOO_PLANKS
        );
    }

    // ==================== EQUIPMENT ====================

    /**
     * Give equipment to the worker for this task.
     * Equipment is tagged as LaborTool to prevent dropping.
     *
     * @param worker The player to give equipment to
     */
    public void giveEquipment(ServerPlayer worker) {
        // Give tool if one is specified
        if (toolGiven != null) {
            ItemStack tool = toolGiven.copy();
            worker.getInventory().add(tool);
        }

        // Always give food for sustenance
        ItemStack bread = new ItemStack(Items.BREAD, 8);
        tagAsLaborTool(bread);
        worker.getInventory().add(bread);

        // Give torches if tool is a pickaxe (for mining)
        if (
            toolGiven != null &&
            (toolGiven.getItem() == Items.IRON_PICKAXE ||
                toolGiven.getItem() == Items.STONE_PICKAXE)
        ) {
            ItemStack torches = new ItemStack(Items.TORCH, 32);
            tagAsLaborTool(torches);
            worker.getInventory().add(torches);
        }
    }

    /**
     * Give equipment using a retrieved tool from cell chest.
     * Used to reuse tools instead of creating new ones.
     *
     * @param worker The player to give equipment to
     * @param retrievedTool The tool retrieved from cell chest
     */
    public void giveRetrievedEquipment(
        ServerPlayer worker,
        ItemStack retrievedTool
    ) {
        // Ensure the retrieved tool has LaborTool tag
        tagAsLaborTool(retrievedTool);
        worker.getInventory().add(retrievedTool);

        // Give consumables (food, torches)
        ItemStack bread = new ItemStack(Items.BREAD, 8);
        tagAsLaborTool(bread);
        worker.getInventory().add(bread);

        if (
            retrievedTool.getItem() == Items.IRON_PICKAXE ||
            retrievedTool.getItem() == Items.STONE_PICKAXE
        ) {
            ItemStack torches = new ItemStack(Items.TORCH, 32);
            tagAsLaborTool(torches);
            worker.getInventory().add(torches);
        }
    }

    /**
     * Reclaim equipment from the worker after task completion.
     * Removes any items tagged as LaborTool belonging to this camp.
     *
     * @param worker The player to reclaim equipment from
     * @return List of reclaimed tool items (for storage in cell chest)
     */
    public List<ItemStack> reclaimEquipment(ServerPlayer worker) {
        List<ItemStack> reclaimedTools = new ArrayList<>();
        var inventory = worker.getInventory();

        for (int i = 0; i < inventory.getContainerSize(); i++) {
            ItemStack stack = inventory.getItem(i);
            if (!stack.isEmpty() && isLaborTool(stack)) {
                // Only store tools (not consumables like bread/torches)
                if (isReusableTool(stack)) {
                    reclaimedTools.add(stack.copy());
                }
                inventory.setItem(i, ItemStack.EMPTY);
            }
        }

        return reclaimedTools;
    }

    /**
     * Collect task items from worker's inventory.
     * Called by maid when task is complete.
     *
     * @param worker The player to collect items from
     * @return List of collected item stacks
     */
    public List<ItemStack> collectItems(ServerPlayer worker) {
        List<ItemStack> collected = new ArrayList<>();
        var inventory = worker.getInventory();

        int remaining = quota;
        for (
            int i = 0;
            i < inventory.getContainerSize() && remaining > 0;
            i++
        ) {
            ItemStack stack = inventory.getItem(i);
            if (!stack.isEmpty() && isAcceptableItem(stack.getItem())) {
                int toTake = Math.min(stack.getCount(), remaining);
                ItemStack taken = stack.split(toTake);
                collected.add(taken);
                remaining -= toTake;
            }
        }

        return collected;
    }

    // ==================== HELPER METHODS ====================

    /**
     * Check if an item is a labor tool from this camp.
     */
    private boolean isLaborTool(ItemStack stack) {
        if (!stack.hasTag()) return false;
        CompoundTag tag = stack.getTag();
        if (tag == null) return false;
        if (!tag.getBoolean("LaborTool")) return false;

        // Check camp ID if we have one
        if (campId != null && tag.hasUUID("CampId")) {
            return campId.equals(tag.getUUID("CampId"));
        }

        return true;
    }

    /**
     * Check if a tool is reusable (should be stored, not discarded).
     */
    private boolean isReusableTool(ItemStack stack) {
        Item item = stack.getItem();
        return (
            item == Items.IRON_PICKAXE ||
            item == Items.STONE_PICKAXE ||
            item == Items.IRON_AXE ||
            item == Items.IRON_SWORD ||
            item == Items.IRON_SHOVEL ||
            item == Items.IRON_HOE
        );
    }

    /**
     * Tag an item as a labor tool belonging to this camp.
     * BUG FIX: Made tools unbreakable to prevent soft-lock when tools break.
     * Problem: Tools breaking left prisoners stuck and punished in loop with no recourse.
     */
    private ItemStack tagAsLaborTool(ItemStack stack) {
        CompoundTag tag = stack.getOrCreateTag();
        tag.putBoolean("LaborTool", true);
        tag.putBoolean("Unbreakable", true); // Prevent tool breakage soft-lock
        if (campId != null) {
            tag.putUUID("CampId", campId);
        }
        return stack;
    }

    // ==================== DESCRIPTION ====================

    /**
     * Get a human-readable description of this task.
     */
    public String getDescription() {
        return String.format("Bring %d %s", quota, getTargetName());
    }

    /**
     * Get the target item name (formatted).
     * For logs, return generic "Logs" instead of specific type.
     */
    public String getTargetName() {
        if (isLogItem(targetItem)) {
            return "Logs";
        }
        if (isPlankItem(targetItem)) {
            return "Planks";
        }
        return new ItemStack(targetItem).getHoverName().getString();
    }

    // ==================== SERIALIZATION ====================

    /**
     * Save task data to NBT.
     */
    public CompoundTag save() {
        CompoundTag tag = new CompoundTag();
        tag.putUUID("id", id);

        // Save target item
        ResourceLocation itemKey = ForgeRegistries.ITEMS.getKey(targetItem);
        if (itemKey != null) {
            tag.putString("targetItem", itemKey.toString());
        }

        tag.putInt("quota", quota);
        tag.putInt("progress", progress);
        tag.putInt("value", value);

        // Save tool if one was given
        if (toolGiven != null) {
            CompoundTag toolTag = new CompoundTag();
            toolGiven.save(toolTag);
            tag.put("toolGiven", toolTag);
        }

        if (campId != null) {
            tag.putUUID("campId", campId);
        }

        return tag;
    }

    /**
     * Load task from NBT. Returns null if invalid.
     */
    @Nullable
    public static LaborTask load(CompoundTag tag) {
        if (
            !tag.contains("targetItem") ||
            !tag.contains("quota") ||
            !tag.contains("value")
        ) {
            return null;
        }

        // Load target item
        String itemKeyStr = tag.getString("targetItem");
        ResourceLocation itemKey = ResourceLocation.tryParse(itemKeyStr);
        if (itemKey == null) return null;

        Item item = ForgeRegistries.ITEMS.getValue(itemKey);
        if (item == null || item == Items.AIR) return null;

        // Load tool (if present)
        ItemStack tool = null;
        if (tag.contains("toolGiven")) {
            tool = ItemStack.of(tag.getCompound("toolGiven"));
        }

        // Create task
        int quota = tag.getInt("quota");
        int value = tag.getInt("value");
        LaborTask task = new LaborTask(item, quota, value, tool);

        // Load progress and camp ID
        if (tag.contains("progress")) {
            task.progress = tag.getInt("progress");
        }
        if (tag.contains("campId")) {
            task.campId = tag.getUUID("campId");
        }

        return task;
    }
}
