// Assets/Scripts/Services/BombService.cs
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Enums;
using Models;
using Services.Interfaces;
using UnityEngine;

namespace Services
{
    public class BombService : IBombService
    {
        public IReadOnlyList<Vector2Int> CollectTriggeredBombs(IReadOnlyList<Vector2Int> matchPositions)
        {
            if (matchPositions == null || matchPositions.Count == 0)
                return Array.Empty<Vector2Int>();
            
            HashSet<Vector2Int> candidates = new HashSet<Vector2Int>();

            foreach (Vector2Int p in matchPositions)
            {
                candidates.Add(p + Vector2Int.left);
                candidates.Add(p + Vector2Int.right);
                candidates.Add(p + Vector2Int.up);
                candidates.Add(p + Vector2Int.down);
            }

            return candidates.ToList();
        }

        public async UniTask DetonateChainAsync(
            IReadOnlyList<Vector2Int> initialBombs,
            Func<Vector2Int, bool> inBounds,
            Func<Vector2Int, Gem> getGemAt,
            Func<Vector2Int, UniTask> destroyAtAsync,
            int radius,
            float bombDelaySeconds,
            float bombSelfDelaySeconds)
        {
            if (initialBombs == null || initialBombs.Count == 0)
                return;

            Queue<Vector2Int> queue = new Queue<Vector2Int>(initialBombs);
            HashSet<Vector2Int> processed = new HashSet<Vector2Int>();

            while (queue.Count > 0)
            {
                Vector2Int bombPos = queue.Dequeue();
                if (processed.Contains(bombPos))
                    continue;

                if (!inBounds(bombPos))
                    continue;

                Gem bomb = getGemAt(bombPos);
                if (bomb is not { Type: GemType.Bomb })
                    continue;

                processed.Add(bombPos);

                int neighborDelayMs = Mathf.Max(0, Mathf.RoundToInt(bombDelaySeconds * 1000f));
                if (neighborDelayMs > 0)
                    await UniTask.Delay(neighborDelayMs);

                foreach (Vector2Int n in DiamondNeighbors(bombPos, radius))
                {
                    if (!inBounds(n))
                        continue;

                    Gem g = getGemAt(n);
                    if (g == null)
                        continue;

                    if (g.Type == GemType.Bomb)
                    {
                        if (!processed.Contains(n))
                            queue.Enqueue(n);
                        continue;
                    }

                    await destroyAtAsync(n);
                }

                int selfDelayMs = Mathf.Max(0, Mathf.RoundToInt(bombSelfDelaySeconds * 1000f));
                if (selfDelayMs > 0)
                    await UniTask.Delay(selfDelayMs);

                Gem stillBomb = getGemAt(bombPos);
                if (stillBomb is { Type: GemType.Bomb })
                    await destroyAtAsync(bombPos);
            }
        }

        private static IEnumerable<Vector2Int> DiamondNeighbors(Vector2Int center, int radius)
        {
            for (int x = -radius; x <= radius; x++)
            {
                int maxY = radius - Mathf.Abs(x);
                for (int y = -maxY; y <= maxY; y++)
                {
                    if (x == 0 && y == 0)
                        continue;

                    yield return new Vector2Int(center.x + x, center.y + y);
                }
            }
        }
    }
}