using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Cirno.Scripts.Resources;
using Godot;
using Godot.Collections;

namespace Cirno.Scripts.Controllers;

public partial class PoolingManager : Node2D
{
    public static PoolingManager Instance { get; private set; }
    
    [Export] public Array<PooledBulletInfo> PoolOnStart { get; private set; }

    [Export] public bool DebugView { get; private set; } = false;

    private readonly System.Collections.Generic.Dictionary<BulletResource, System.Collections.Generic.List<Bullet>>
        _activeBullets = new();
    
    private readonly System.Collections.Generic.Dictionary<BulletResource, System.Collections.Generic.List<Bullet>>
        _inactiveBullets = new();

    public override void _Ready()
    {
        Instance = this;
    }
    
    public Bullet SpawnBullet(BulletResource bulletResource, bool active = true)
    {
        // Look for bullet among the inactive ones
        // If present move it to active, set it as active and return it
        // Otherwise spawn it and add it to active and return it

        var activeBullets = GetActiveBulletsList(bulletResource);
        var inactiveBullets = GetInactiveBulletsList(bulletResource);

        var bullet = inactiveBullets.Count > 0 ? inactiveBullets.First() : InstantiateBullet(bulletResource);

        if (active)
        {
            inactiveBullets.Remove(bullet);
            activeBullets.Add(bullet);
            bullet.Enable();
        }
        else
        {
            activeBullets.Add(bullet);
            inactiveBullets.Remove(bullet);
            bullet.Disable();
        }

        return bullet;
    }

    public IEnumerable<Bullet> GetAllActiveBullets()
    {
        return _activeBullets.Values.SelectMany(list => list);
    }
    
    public IEnumerable<Bullet> GetAllInActiveBullets()
    {
        return _activeBullets.Values.SelectMany(list => list);
    }
    
    public List<Bullet> GetActiveBulletsList(BulletResource resource)
    {
        return GetOrCreateList(_activeBullets, resource);
    }
    
    private List<Bullet> GetInactiveBulletsList(BulletResource resource)
    {
        return GetOrCreateList(_inactiveBullets, resource);
    }

    public void DisableBullet(Bullet bullet)
    {
        var activeBulletsList = GetActiveBulletsList(bullet.BulletInfo.OriginalBulletResource);
        
        var inactiveBulletsList = GetInactiveBulletsList(bullet.BulletInfo.OriginalBulletResource);

        bullet.Disable(!DebugView);
        
        activeBulletsList.Remove(bullet);
        
        inactiveBulletsList.Add(bullet);
    }

    public void DisableAllBullets()
    {
        var allActiveBullets = GetAllActiveBullets().ToImmutableList();

        foreach (var bullet in allActiveBullets)
        {
            var inactiveBulletsList = GetInactiveBulletsList(bullet.BulletInfo.OriginalBulletResource);
            
            bullet.Disable(!DebugView);
            
            inactiveBulletsList.Add(bullet);
        }

        foreach (var activeBulletsList in _activeBullets)
        {
            activeBulletsList.Value.Clear();
        }
        
    }
    
    private List<Bullet> GetOrCreateList(System.Collections.Generic.Dictionary<BulletResource, List<Bullet>> dict, BulletResource resource)
    {
        if (dict.TryGetValue(resource, out var list)) return list;
        list = [];
        dict[resource] = list;
        return list;
    }
    
    

    private Bullet InstantiateBullet(BulletResource bulletData)
    {
        var bullet = this.CreateChild<Bullet>(bulletData.BulletScene);
        return bullet;
    }
    
}

public partial class PooledBulletInfo : Resource
{
    public BulletResource Bullet { get; private set; }
    public int Amount { get; private set; }
}