using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Enums;
using Models.Interfaces;
using Services.Interfaces;
using Structs;
using UnityEngine;

namespace Services {
    public class MatchService : IMatchService {
        private readonly IGameBoard gameBoard;
        
        
        private List<Gem> currentMatches = new List<Gem>();
        public List<Gem> CurrentMatches => this.currentMatches;
        
        public MatchService(IGameBoard gameBoard) {
            this.gameBoard = gameBoard;
        }
        
        public bool MatchesAt(Vector2Int positionToCheck, GemType gemTypeToCheck) {
            Gem[,] gems = this.gameBoard.GemsGrid;

            // We don't prevent spawning bombs via this rule (and bombs shouldn't be treated as a normal color here).
            if (gemTypeToCheck == GemType.Bomb)
                return false;

            // Check horizontal: would placing gemTypeToCheck at positionToCheck create XXX with 2-left?
            if (positionToCheck.x > 1) {
                Gem left1 = gems[positionToCheck.x - 1, positionToCheck.y];
                Gem left2 = gems[positionToCheck.x - 2, positionToCheck.y];

                if (left1 != null && left2 != null &&
                    left1.MatchColor == gemTypeToCheck &&
                    left2.MatchColor == gemTypeToCheck)
                    return true;
            }

            // Check vertical: would placing gemTypeToCheck at positionToCheck create XXX with 2-down?
            if (positionToCheck.y > 1) {
                Gem down1 = gems[positionToCheck.x, positionToCheck.y - 1];
                Gem down2 = gems[positionToCheck.x, positionToCheck.y - 2];

                if (down1 != null && down2 != null &&
                    down1.MatchColor == gemTypeToCheck &&
                    down2.MatchColor == gemTypeToCheck)
                    return true;
            }

            return false;
        }

        public UniTask<List<Vector2Int>> GetMatchPositionsAsync(List<Vector2Int> protectedPositions) {
            List<Vector2Int> matchPositions = new List<Vector2Int>(CurrentMatches.Count);
            for (int i = 0; i < CurrentMatches.Count; i++) {
                Gem match = CurrentMatches[i];
                if (match == null) continue;

                Vector2Int pos = match.Position;
                if (protectedPositions != null && protectedPositions.Contains(pos))
                    continue;

                matchPositions.Add(pos);
            }
            
            return UniTask.FromResult(matchPositions);
        }

        public void FindAllMatches() {
            this.currentMatches.Clear();

            for (int x = 0; x < this.gameBoard.Width; x++)
                for (int y = 0; y < this.gameBoard.Height; y++) {
                    Gem currentGem = this.gameBoard.GemsGrid[x, y];
                    if (currentGem == null)
                        continue;

                    if (x > 0 && x < this.gameBoard.Width - 1) {
                        Gem leftGem = this.gameBoard.GemsGrid[x - 1, y];
                        Gem rightGem = this.gameBoard.GemsGrid[x + 1, y];
                        if (leftGem != null && rightGem != null) {
                            if (leftGem.MatchColor == currentGem.MatchColor && rightGem.MatchColor == currentGem.MatchColor) {
                                this.currentMatches.Add(currentGem);
                                this.currentMatches.Add(leftGem);
                                this.currentMatches.Add(rightGem);
                            }
                        }
                    }

                    if (y > 0 && y < this.gameBoard.Height - 1) {
                        Gem aboveGem = this.gameBoard.GemsGrid[x, y - 1];
                        Gem bellowGem = this.gameBoard.GemsGrid[x, y + 1];
                        if (aboveGem != null && bellowGem != null) {
                            if (aboveGem.MatchColor == currentGem.MatchColor && bellowGem.MatchColor == currentGem.MatchColor) {
                                this.currentMatches.Add(currentGem);
                                this.currentMatches.Add(aboveGem);
                                this.currentMatches.Add(bellowGem);
                            }
                        }
                    }
                }

            if (this.currentMatches.Count > 0)
                this.currentMatches = this.currentMatches.Distinct().ToList();
        }
    }
}