using System.Collections.Generic;
using Godot;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;

namespace Mr.BrickAdventures.scripts.components;

[GlobalClass]
public partial class PlayerController : CharacterBody2D
{
    [Export] private Node MovementAbilitiesContainer { get; set; }
    [Export] public MovementPreset DefaultPreset { get; set; }

    [Signal] public delegate void JumpInitiatedEventHandler();
    [Signal] public delegate void MovementAbilitiesChangedEventHandler();

    public Vector2 LastDirection { get; private set; } = Vector2.Right;
    public Vector2 PreviousVelocity { get; private set; } = Vector2.Zero;

    private List<MovementAbility> _abilities = [];
    private PlayerInputHandler _inputHandler;

    public IReadOnlyList<MovementAbility> GetActiveAbilities() => _abilities;

    public override void _Ready()
    {
        SkillManager.Instance?.RegisterPlayer(this);

        // Wire HealthComponent signals to global EventBus events
        var health = GetNodeOrNull<HealthComponent>("HealthComponent");
        if (health != null)
        {
            health.Death += () => EventBus.EmitPlayerDied(GlobalPosition);
            health.HealthChanged += (delta, total) =>
            {
                if (delta < 0f) EventBus.EmitPlayerDamaged(Mathf.Abs(delta), total, GlobalPosition);
                else EventBus.EmitPlayerHealed(delta, total, GlobalPosition);
            };
        }

        _inputHandler = GetNode<PlayerInputHandler>("PlayerInputHandler");

        if (DefaultPreset != null)
            ApplyPreset(DefaultPreset);

        EmitSignalMovementAbilitiesChanged();
        EventBus.EmitPlayerSpawned(this);
    }

    public override void _PhysicsProcess(double delta)
    {
        var velocity = Velocity;

        foreach (var ability in _abilities)
        {
            velocity = ability.ProcessMovement(velocity, delta);
        }

        if (_inputHandler.MoveDirection.X != 0)
        {
            LastDirection = new Vector2(_inputHandler.MoveDirection.X > 0 ? 1 : -1, 0);
        }

        PreviousVelocity = Velocity;
        Velocity = velocity;
        MoveAndSlide();
    }

    public void ApplyPreset(MovementPreset preset)
    {
        if (preset == null) return;
        ClearMovementAbilities();
        Velocity = Vector2.Zero;
        foreach (var scene in preset.Abilities)
        {
            if (scene != null)
                AddAbility(scene.Instantiate<MovementAbility>());
        }
        EmitSignalMovementAbilitiesChanged();
    }

    public void AddAbility(MovementAbility ability)
    {
        MovementAbilitiesContainer.AddChild(ability);
        ability.Initialize(this);
        _abilities.Add(ability);
    }

    public void ClearMovementAbilities()
    {
        foreach (var ability in _abilities)
        {
            ability.QueueFree();
        }
        _abilities.Clear();
    }

    public void RemoveAbility<T>() where T : MovementAbility
    {
        for (var i = _abilities.Count - 1; i >= 0; i--)
        {
            if (_abilities[i] is T)
            {
                var ability = _abilities[i];
                _abilities.RemoveAt(i);
                ability.QueueFree();
                break;
            }
        }
    }
}
