using System.Collections.Generic;
using System.Linq;
using Cirno.Scripts.Actors;
using Cirno.Scripts.Components.Actors;
using Cirno.Scripts.Controllers;
using Godot;

namespace Cirno.Scripts.Components.FSM.Player;

public partial class FreezeModule : ModuleBase<PlayerState, CharacterBody2D>
{
    [Export] public float ResourceCost { get; private set; } = 15f;

    [Export] public float FreezeRadius { get; private set; } = 64f;
    [Export] public double Cooldown { get; private set; } = 0.5f;
    [Export] public double IceLife { get; private set; } = 4f;
    [Export] public PackedScene IceScene { get; private set; }

    [ExportGroup("Providers")] [Export] public ActorResourceProvider Shield { get; private set; }

    [Export] public InputProvider InputProvider { get; private set; }

    public bool Enabled { get; set; } = false;

    private double _cooldownTimer = 0;
    
    private IStateMachine<PlayerState, CharacterBody2D> _machine;

    public override void EnterState(PlayerState state)
    {
        Enabled = true;
    }

    public override void ExitState(PlayerState state)
    {
        Enabled = false;
    }
    
    public override void Init(IStateMachine<PlayerState, CharacterBody2D> machine)
    {
        _machine = machine;
    }

    public override void Process(double delta)
    {
        if (!Enabled) return;
        // TODO: Handle cooldown
    }

    public override void PhysicsProcess(double delta)
    {
        if (!Enabled) return;
        if (InputProvider.GetFreezeJustPressed())
        {
            if (Shield.CurrentResource >= ResourceCost)
            {
                Shield.CurrentResource -= ResourceCost;
                FreezeBullets();
            }
        }
    }

    private void FreezeBullets()
    {
        var bullets = GetNearbyBullets();
        foreach (var bullet in bullets)
        {
            bullet.Freeze();
            var ice = bullet.CreateSibling<Ice>(IceScene);
            ice.Life = IceLife;
            ice.FreezeModule = this;
        }
    }

    private List<Bullet> GetNearbyBullets()
    {
        return (from child in PoolingManager.Instance.GetAllActiveBullets()
            where child is not null
            where child.Enabled // Could be redundant but better check in case of errors
            where child.BulletOwner is not BulletOwner.Player
            where !child.IsFrozen
            where child.BulletInfo.Freezable
            let distance = _machine.MainObject.GlobalPosition.DistanceTo(child.GlobalPosition)
            where distance <= FreezeRadius
            select child).ToList();
    }
}