using Godot;
using System.Collections.Generic;
using System.Linq;

namespace AceFieldNewHorizon.Scripts.System;

public partial class ResourceManager : Node
{
    // Dictionary to store resources with their IDs and quantities
    private readonly Dictionary<string, int> _resources = new();
    private static readonly StringName LoggerName = "ResourceManager";

    // Event for when resource amounts change
    [Signal]
    public delegate void OnResourceChangedEventHandler(string resourceId, int newAmount);
    
    // Event for when resources are picked up (added)
    [Signal]
    public delegate void OnResourcePickedUpEventHandler(string resourceId, int amount);

    public override void _Ready()
    {
        base._Ready();
        GD.Print($"[{LoggerName}] ResourceManager initialized");
    }

    // Add resources of a specific type
    public void AddResource(string resourceId, int amount = 1)
    {
        if (amount <= 0) 
        {
            GD.PushWarning($"[{LoggerName}] Attempted to add non-positive amount ({amount}) for resource: {resourceId}");
            return;
        }
        
        bool isNew = !_resources.ContainsKey(resourceId);
        
        if (!_resources.TryAdd(resourceId, amount))
        {
            var oldAmount = _resources[resourceId];
            _resources[resourceId] += amount;
            GD.Print($"[{LoggerName}] Added {amount} {resourceId}. New total: {_resources[resourceId]} (was {oldAmount})");
        }
        else
        {
            GD.Print($"[{LoggerName}] Added new resource: {resourceId} with amount: {amount}");
        }

        EmitSignal(nameof(OnResourceChanged), resourceId, _resources[resourceId]);
        EmitSignal(nameof(OnResourcePickedUp), resourceId, amount);
    }

    // Remove resources of a specific type
    public bool RemoveResource(string resourceId, int amount = 1)
    {
        if (amount <= 0) 
        {
            GD.PushWarning($"[{LoggerName}] Attempted to remove non-positive amount ({amount}) for resource: {resourceId}");
            return false;
        }
        
        if (!_resources.ContainsKey(resourceId))
        {
            GD.Print($"[{LoggerName}] Failed to remove {amount} {resourceId}: Resource not found");
            return false;
        }
        
        if (_resources[resourceId] < amount)
        {
            GD.Print($"[{LoggerName}] Insufficient {resourceId}. Requested: {amount}, Available: {_resources[resourceId]}");
            return false;
        }
        
        var oldAmount = _resources[resourceId];
        _resources[resourceId] -= amount;
        
        GD.Print($"[{LoggerName}] Removed {amount} {resourceId}. New total: {_resources[resourceId]} (was {oldAmount})");
        
        // Remove the entry if quantity reaches zero
        if (_resources[resourceId] <= 0)
        {
            _resources.Remove(resourceId);
            GD.Print($"[{LoggerName}] Removed resource entry for {resourceId} (reached zero)");
            EmitSignal(nameof(OnResourceChanged), resourceId, 0);
        }
        else
        {
            EmitSignal(nameof(OnResourceChanged), resourceId, _resources[resourceId]);
        }
        
        return true;
    }

    // Get the current amount of a specific resource
    public int GetResourceAmount(string resourceId)
    {
        // Silently return 0 for non-existent resources
        return _resources.GetValueOrDefault(resourceId, 0);
    }

    // Check if there are at least 'amount' of a specific resource
    public bool HasResource(string resourceId, int amount = 1)
    {
        if (amount <= 0) return true; // Always true for zero or negative amounts
        
        var exists = _resources.TryGetValue(resourceId, out var currentAmount);
        var hasEnough = exists && currentAmount >= amount;
        
        return hasEnough;
    }

    // Get all resources as a read-only dictionary
    public IReadOnlyDictionary<string, int> GetAllResources()
    {
        GD.Print($"[{LoggerName}] Getting all resources. Total types: {_resources.Count}");
        return new Dictionary<string, int>(_resources);
    }

    // Clear all resources
    public void Clear()
    {
        GD.Print($"[{LoggerName}] Clearing all resources. Total types before clear: {_resources.Count}");
        
        var keys = new List<string>(_resources.Keys);
        foreach (var resourceId in keys)
        {
            _resources[resourceId] = 0;
            EmitSignal(nameof(OnResourceChanged), resourceId, 0);
        }
        _resources.Clear();
        
        GD.Print($"[{LoggerName}] All resources cleared");
    }

    // Helper method to log current resource state
    public void LogResourceState()
    {
        if (_resources.Count == 0)
        {
            GD.Print($"[{LoggerName}] No resources currently stored");
            return;
        }

        GD.Print($"[{LoggerName}] Current resource state:");
        foreach (var (id, amount) in _resources.OrderBy(x => x.Key))
        {
            GD.Print($"  - {id}: {amount}");
        }
    }
}