using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using Enums;
using Models.Interfaces;
using ScriptableObjects;
using Services.Interfaces;
using UnityEngine;
using Utils;
using Views;
using Object = UnityEngine.Object;
using Random = UnityEngine.Random;

namespace Services {
    public class GameBoardService : IGameBoardService {
        private IGameBoard gameBoard;
        private GameVariables gameVariables;
        private IMatchService matchService;
        private IScoreService scoreService;
        private IObjectPool<GemView> objectPool;
        private Transform gemsHolder;
        
        public GameBoardService(IGameBoard gameBoard, GameVariables gameVariables, IMatchService matchService,  IScoreService scoreSerivce, IObjectPool<GemView> objectPool, Transform gemsHolder) {
            this.gameBoard = gameBoard;
            this.gameVariables = gameVariables;
            this.matchService = matchService;
            this.scoreService = scoreSerivce;
            this.objectPool = objectPool;
            this.gemsHolder = gemsHolder;
        }
        
        public void Setup() {
            Debug.Log("Setting up the board");
            for (int x = 0; x < this.gameBoard.Width; x++)
                for (int y = 0; y < this.gameBoard.Height; y++)
                {
                    Vector2 position = new Vector2(x, y);
                    GameObject backgroundTile = Object.Instantiate(this.gameVariables.bgTilePrefabs, position, Quaternion.identity);
                    backgroundTile.transform.SetParent(this.gemsHolder);
                    backgroundTile.name = "BG Tile - " + x + ", " + y;
                    
                    int gemToUse = RandomUtils.RandomGemTypeAsInt();

                    int iterations = 0;
                    while (this.matchService.MatchesAt(new Vector2Int(x, y), (GemType)gemToUse) && iterations < 100)
                    {
                        gemToUse = RandomUtils.RandomGemTypeAsInt();
                        iterations++;
                    }
                    
                    SpawnGem(new Vector2Int(x, y), this.gameVariables.gemsPrefabs[gemToUse], (GemType)gemToUse);
                }
        }

        public void SpawnGem(Vector2Int position, GemView gemPrefab, GemType gemType) {
            Debug.Log("Spawning gem at " + position + " with type " + gemType + "");
            if (Random.Range(0, 100f) < this.gameVariables.bombChance)
                gemPrefab = this.gameVariables.bombPrefab;
            
            GemView gemView = this.objectPool.Get(gemType, position, this.gameVariables.dropHeight);
            gemView.name = "Gem - " + position.x + ", " + position.y;
            SetGem(new Vector2Int(position.x,position.y), new Gem(gemType, position));
        }

        public void SetGem(Vector2Int position, Gem gem) {
            this.gameBoard.SetGemAt(new Vector2Int(position.x, position.y), gem);
        }

        public Gem GetGem(Vector2Int position) {
            return this.gameBoard.GetGemAt(position);
        }

        public void DestroyMatches() {
            for (int i = 0; i < this.matchService.CurrentMatches.Count; i++)
                if (this.matchService.CurrentMatches[i] != null)
                {
                    this.scoreService.ScoreCheck(this.matchService.CurrentMatches[i].ScoreValue);
                    DestroyMatchedGems(this.matchService.CurrentMatches[i].Position);
                }

            MoveGemsDown();
        }

        public async UniTask MoveGemsDown() {
            await UniTask.Delay(2);
            // why the delay though?
            // yield return new WaitForSeconds(.2f);

            int nullCounter = 0;
            for (int x = 0; x < this.gameBoard.Width; x++)
            {
                for (int y = 0; y < this.gameBoard.Height; y++)
                {
                    Gem currentGem = this.gameBoard.GetGemAt(new Vector2Int(x, y));
                    if (currentGem == null)
                    {
                        nullCounter++;
                    }
                    else if (nullCounter > 0)
                    {
                        currentGem.SetPosition(new Vector2Int(currentGem.Position.x, currentGem.Position.y - nullCounter));
                        SetGem(currentGem.Position, currentGem);
                        SetGem(new Vector2Int(x,y), null);
                    }
                }
                nullCounter = 0;
            }

            await FillBoard();
        }

        public async UniTask FillBoard() {
            await UniTask.Delay(5);
            RefillBoard();
            await UniTask.Delay(5);
            this.matchService.FindAllMatches();
            if (this.matchService.CurrentMatches.Count > 0)
            {
                await UniTask.Delay(5);
                DestroyMatches();
            }
            else
            {
                await UniTask.Delay(5);
                // currentState = GameState.Move;
            }
        }

        public void RefillBoard() {
            for (int x = 0; x < this.gameBoard.Width; x++)
            {
                for (int y = 0; y < this.gameBoard.Height; y++)
                {
                    Gem currentGem = this.gameBoard.GetGemAt(new Vector2Int(x,y));
                    if (currentGem == null) {
                        int gemToUse = RandomUtils.RandomGemTypeAsInt();
                        SpawnGem(new Vector2Int(x, y), this.gameVariables.gemsPrefabs[gemToUse], (GemType)gemToUse);
                    }
                }
            }
            CheckMisplacedGems();
        }

        public void CheckMisplacedGems() {
            List<GemView> gemsViews = GemsViews();
            
            for (int x = 0; x < this.gameBoard.Width; x++)
            {
                for (int y = 0; y < this.gameBoard.Height; y++)
                {
                    Gem currentGem = this.gameBoard.GetGemAt(new Vector2Int(x,y));
                    GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem);

                    if (gemView != null) {
                        gemsViews.Remove(gemView);
                    }
                }
            }

            foreach (GemView g in gemsViews)
                Object.Destroy(g.gameObject);
        }

        public void DestroyMatchedGems(Vector2Int position) {
            List<GemView> gemsViews = GemsViews();
            Gem currentGem = this.gameBoard.GetGemAt(position);
            if (currentGem != null)
            {
                GemView gemView = gemsViews.FirstOrDefault(gv => gv.Gem == currentGem);
                if(this.gameVariables.destroyEffectPrefabs.Length > 0)
                    Object.Instantiate(this.gameVariables.destroyEffectPrefabs[(int)currentGem.Type], new Vector2(position.x, position.y), Quaternion.identity);

                Object.Destroy(gemView!.gameObject);
                SetGem(position, null);
            }
        }
        
        private List<GemView> GemsViews() {
            return this.gemsHolder.GetComponentsInChildren<GemView>().ToList();
        }
    }
}