using System.Collections;
using AceFieldNewHorizon.Scripts.Entities;
using Godot;

namespace AceFieldNewHorizon.Scripts.System;

public partial class ItemPickup : Area2D
{
    [Signal]
    public delegate void StackMergedEventHandler(string itemId, int totalQuantity);

    public const string PickupGroupName = "ItemPickupTarget";

    [Export] public string ItemId { get; set; } = "";
    [Export] public int Quantity { get; set; } = 1;
    [Export] public bool Infinite { get; set; } = false;
    [Export] public float MagnetRange { get; set; } = 64f;

    public int GetItemQuantity(string itemId)
    {
        if (itemId == ItemId)
            return Quantity;
        return 0;
    }

    public void SetItemQuantity(string itemId, int newQuantity)
    {
        if (itemId == ItemId)
        {
            Quantity = newQuantity;
            // Update the quantity label if it exists
            if (_quantityLabel != null)
            {
                if (Quantity > 1)
                    _quantityLabel.Text = Quantity.ToString();
                else
                    _quantityLabel.Text = string.Empty;
            }
        }
    }

    public bool HasItem(string itemId)
    {
        return itemId == ItemId;
    }

    public void AddItem(string itemId, int amount)
    {
        if (itemId == ItemId)
        {
            Quantity += amount;
            // Update the quantity label if it exists
            if (_quantityLabel != null)
            {
                if (Quantity > 1)
                    _quantityLabel.Text = Quantity.ToString();
                else
                    _quantityLabel.Text = string.Empty;
            }
        }
    }

    private Sprite2D _sprite;
    private Label _quantityLabel;
    private Sprite2D _shadowSprite;
    private Node2D _playerTarget;

    // Called when the node enters the scene tree
    public override void _Ready()
    {
        BodyEntered += OnEntered;
        AreaEntered += OnEntered;

        _sprite = GetNode<Sprite2D>("Sprite2D");
        UpdateTexture();

        // Get the Label node for quantity
        if (HasNode("Label"))
        {
            _quantityLabel = GetNode<Label>("Label");
            if (Quantity > 1)
                _quantityLabel.Text = Quantity.ToString();
            else
                _quantityLabel.Text = string.Empty;
        }

        // Add or update shadow sprite
        if (HasNode("ShadowSprite"))
        {
            _shadowSprite = GetNode<Sprite2D>("ShadowSprite");
            _shadowSprite.Texture = _sprite.Texture;
            _shadowSprite.Modulate = new Color(0, 0, 0, 0.5f);
            _shadowSprite.Position = _sprite.Position + new Vector2(0, 6);
            _shadowSprite.ZIndex = _sprite.ZIndex - 1;
        }
        else
        {
            _shadowSprite = new Sprite2D();
            _shadowSprite.Scale = _sprite.Scale;
            _shadowSprite.Name = "ShadowSprite";
            _shadowSprite.Texture = _sprite.Texture;
            _shadowSprite.Modulate = new Color(0, 0, 0, 0.5f);
            _shadowSprite.Position = _sprite.Position + new Vector2(0, 6);
            _shadowSprite.ZIndex = _sprite.ZIndex - 1;
            AddChild(_shadowSprite);
        }

        _playerTarget = GetTree().GetFirstNodeInGroup(PickupGroupName) as Node2D;
        if (_playerTarget == null)
            _playerTarget = GetTree().GetFirstNodeInGroup("Player") as Node2D;
    }

    public override void _Process(double delta)
    {
        if (_playerTarget != null)
        {
            var distance = Position.DistanceTo(_playerTarget.Position);
            const float speed = 10f;
            if (distance <= MagnetRange)
                Position = Position.Lerp(_playerTarget.Position, (float)delta * speed);
        }
    }

    private void UpdateTexture()
    {
        var file = FileAccess.Open("res://Data/ItemTextures.json", FileAccess.ModeFlags.Read);
        if (file == null)
        {
            GD.PrintErr("Failed to open ItemTextures.json");
            return;
        }

        var text = file.GetAsText();
        file.Close();

        var json = new Json();
        var err = json.Parse(text);
        if (err != Error.Ok)
        {
            GD.PrintErr(json.GetErrorMessage());
            return;
        }

        var dict = json.Data.AsGodotDictionary();
        if (!dict.TryGetValue(ItemId, out var value))
            return;

        var texturePath = value.AsString();
        if (string.IsNullOrEmpty(texturePath))
            return;

        var texture = GD.Load<Texture2D>(texturePath);
        if (texture == null)
            return;

        _sprite.Texture = texture;
    }

    private void OnEntered(Node body)
    {
        if (body is ItemPickup itemStack)
        {
            // Only process the merge for the item with the lower instance ID to prevent double merging
            if (itemStack.ItemId != ItemId || GetInstanceId() > body.GetInstanceId()) 
                return;
            
            // Get current quantity and add to it
            var currentQuantity = itemStack.GetItemQuantity(ItemId);
            var newQuantity = currentQuantity + Quantity;
            itemStack.SetItemQuantity(ItemId, newQuantity);

            // Emit signal for stack merge
            EmitSignal(nameof(StackMerged), ItemId, newQuantity);
            QueueFree();
        }
        else if (body.IsInGroup(PickupGroupName))
        {
            if (body is Player player)
            {
                player.AddItem(ItemId, Quantity);
            }

            if (!Infinite)
                QueueFree();
        }
    }
}