using System;
using System.Collections.Generic;
using Minesweeper.Config;
using Minesweeper.Core;
using Minesweeper.Presentation.Factories;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

namespace Minesweeper.Presentation.Views
{
    public sealed class GameView : MonoBehaviour, IGameView
    {
        [SerializeField] private GameObject gameRoot;
        [SerializeField] private GameObject pauseRoot;
        [SerializeField] private GameObject resultRoot;
        [SerializeField] private RectTransform boardPanel;
        [SerializeField] private GridLayoutGroup gridLayoutGroup;
        [SerializeField] private Button pauseButton;
        [SerializeField] private Button restartButton;
        [SerializeField] private Button resumeButton;
        [SerializeField] private Button mainMenuButton;
        [SerializeField] private Button resultRestartButton;
        [SerializeField] private Button resultMainMenuButton;
        [SerializeField] private TMP_Text resultText;
        [SerializeField] private TMP_Text timerText;
        [SerializeField] private TMP_Text mineText;
        [SerializeField] private MinesweeperUiConfig uiConfig;

        private readonly Dictionary<int, CellView> cellsByCoordinate = new Dictionary<int, CellView>();
        private bool boardInputEnabled = true;
        private float currentPixelsPerUnitMultiplier = 1f;

        public event Action RestartRequested;
        public event Action GoToMenuRequested;
        public event Action PauseRequested;
        public event Action ResumeRequested;
        public event Action CellPressStarted;
        public event Action CellPressEnded;
        public event Action<int, int> CellOpenRequested;
        public event Action<int, int> CellFlagRequested;

        private void Awake()
        {
            if (gameRoot == null)
            {
                gameRoot = gameObject;
            }
        }

        private void OnEnable()
        {
            AddButtonListeners();
        }

        private void OnDisable()
        {
            RemoveButtonListeners();
        }

        public void ShowGame()
        {
            gameRoot.SetActive(true);
            SetBoardRootActive(true);
        }

        public void HideGame()
        {
            gameRoot.SetActive(true);
            SetBoardRootActive(false);
        }

        public void ShowPause()
        {
            if (pauseRoot != null)
            {
                pauseRoot.SetActive(true);
            }
        }

        public void HidePause()
        {
            if (pauseRoot != null)
            {
                pauseRoot.SetActive(false);
            }
        }

        public void ShowResult(GameState state)
        {
            if (resultRoot != null)
            {
                resultRoot.SetActive(true);
            }

            if (resultText != null)
            {
                resultText.text = state == GameState.Won ? "YOU WIN" : "GAME OVER";
            }
        }

        public void HideResult()
        {
            if (resultRoot != null)
            {
                resultRoot.SetActive(false);
            }
        }

        public void SetTimer(float seconds)
        {
            if (timerText != null)
            {
                timerText.text = Mathf.FloorToInt(seconds).ToString("00000");
            }
        }

        public void SetMineCount(int minesCount)
        {
            if (mineText != null)
            {
                mineText.text = minesCount.ToString("00000");
            }
        }

        public void BindConfig(MinesweeperUiConfig config)
        {
            uiConfig = config;
        }

        public void RebuildBoard(IReadOnlyList<BoardCellData> cells, int width, int height, ICellViewFactory cellViewFactory)
        {
            ClearBoard();
            ConfigureGrid(width, height);

            for (var i = 0; i < cells.Count; i++)
            {
                CreateCell(cells[i], cellViewFactory);
            }

            RefreshBoard(cells);
        }

        public void RefreshBoard(IReadOnlyList<BoardCellData> cells)
        {
            for (var i = 0; i < cells.Count; i++)
            {
                var cell = cells[i];
                if (cellsByCoordinate.TryGetValue(ToKey(cell.X, cell.Y), out var view))
                {
                    view.Render(cell, uiConfig, currentPixelsPerUnitMultiplier);
                }
            }
        }

        public void SetBoardInputEnabled(bool enabled)
        {
            boardInputEnabled = enabled;
            foreach (var cell in cellsByCoordinate.Values)
            {
                cell.SetInputEnabled(enabled);
            }
        }

        private void AddButtonListeners()
        {
            if (pauseButton != null)
            {
                pauseButton.onClick.AddListener(OnPauseClicked);
            }

            if (restartButton != null)
            {
                restartButton.onClick.AddListener(OnRestartClicked);
            }

            if (resumeButton != null)
            {
                resumeButton.onClick.AddListener(OnResumeClicked);
            }

            if (mainMenuButton != null)
            {
                mainMenuButton.onClick.AddListener(OnMainMenuClicked);
            }

            if (resultRestartButton != null)
            {
                resultRestartButton.onClick.AddListener(OnRestartClicked);
            }

            if (resultMainMenuButton != null)
            {
                resultMainMenuButton.onClick.AddListener(OnMainMenuClicked);
            }
        }

        private void RemoveButtonListeners()
        {
            if (pauseButton != null)
            {
                pauseButton.onClick.RemoveListener(OnPauseClicked);
            }

            if (restartButton != null)
            {
                restartButton.onClick.RemoveListener(OnRestartClicked);
            }

            if (resumeButton != null)
            {
                resumeButton.onClick.RemoveListener(OnResumeClicked);
            }

            if (mainMenuButton != null)
            {
                mainMenuButton.onClick.RemoveListener(OnMainMenuClicked);
            }

            if (resultRestartButton != null)
            {
                resultRestartButton.onClick.RemoveListener(OnRestartClicked);
            }

            if (resultMainMenuButton != null)
            {
                resultMainMenuButton.onClick.RemoveListener(OnMainMenuClicked);
            }
        }

        private void SetBoardRootActive(bool active)
        {
            if (boardPanel != null)
            {
                boardPanel.gameObject.SetActive(active);
            }
        }

        private void ConfigureGrid(int width, int height)
        {
            if (gridLayoutGroup == null || boardPanel == null)
            {
                return;
            }

            Canvas.ForceUpdateCanvases();

            var parentRect = boardPanel.parent as RectTransform;
            var rect = parentRect != null ? parentRect.rect : boardPanel.rect;
            var panelWidth = rect.width > 0f ? rect.width : uiConfig.ReferenceCellSize * width;
            var panelHeight = rect.height > 0f ? rect.height : uiConfig.ReferenceCellSize * height;
            var cellSize = CalculateCellSize(panelWidth, panelHeight, width, height);
            var padding = cellSize * uiConfig.BoardPaddingRatio;
            var spacing = cellSize * uiConfig.GridSpacingRatio;

            boardPanel.anchorMin = Vector2.zero;
            boardPanel.anchorMax = Vector2.one;
            boardPanel.offsetMin = new Vector2(padding, padding);
            boardPanel.offsetMax = new Vector2(-padding, -padding);

            gridLayoutGroup.constraint = GridLayoutGroup.Constraint.FixedColumnCount;
            gridLayoutGroup.constraintCount = width;
            gridLayoutGroup.padding = new RectOffset();
            gridLayoutGroup.spacing = new Vector2(spacing, spacing);
            gridLayoutGroup.cellSize = new Vector2(cellSize, cellSize);
            currentPixelsPerUnitMultiplier = uiConfig.ReferenceCellSize / cellSize;
        }

        private float CalculateCellSize(float panelWidth, float panelHeight, int width, int height)
        {
            var widthUnits = width + 2f * uiConfig.BoardPaddingRatio + Mathf.Max(0, width - 1) * uiConfig.GridSpacingRatio;
            var heightUnits = height + 2f * uiConfig.BoardPaddingRatio + Mathf.Max(0, height - 1) * uiConfig.GridSpacingRatio;
            var cellByWidth = panelWidth / widthUnits;
            var cellByHeight = panelHeight / heightUnits;
            return Mathf.Max(uiConfig.MinimumCellSize, Mathf.Floor(Mathf.Min(cellByWidth, cellByHeight)));
        }

        private void CreateCell(BoardCellData cell, ICellViewFactory cellViewFactory)
        {
            var view = cellViewFactory.CreateCell(cell, gridLayoutGroup.transform);
            view.SetInputEnabled(boardInputEnabled);
            view.OpenRequested += OnCellOpenRequested;
            view.FlagRequested += OnCellFlagRequested;
            view.PressStarted += OnCellPressStarted;
            view.PressEnded += OnCellPressEnded;
            cellsByCoordinate[ToKey(cell.X, cell.Y)] = view;
        }

        private void ClearBoard()
        {
            foreach (var cell in cellsByCoordinate.Values)
            {
                if (cell != null)
                {
                    cell.OpenRequested -= OnCellOpenRequested;
                    cell.FlagRequested -= OnCellFlagRequested;
                    cell.PressStarted -= OnCellPressStarted;
                    cell.PressEnded -= OnCellPressEnded;
                }
            }

            cellsByCoordinate.Clear();

            if (gridLayoutGroup == null)
            {
                return;
            }

            for (var i = gridLayoutGroup.transform.childCount - 1; i >= 0; i--)
            {
                Destroy(gridLayoutGroup.transform.GetChild(i).gameObject);
            }
        }

        private void OnCellOpenRequested(int x, int y)
        {
            CellOpenRequested?.Invoke(x, y);
        }

        private void OnCellFlagRequested(int x, int y)
        {
            CellFlagRequested?.Invoke(x, y);
        }

        private void OnCellPressStarted()
        {
            CellPressStarted?.Invoke();
        }

        private void OnCellPressEnded()
        {
            CellPressEnded?.Invoke();
        }

        private void OnPauseClicked()
        {
            PauseRequested?.Invoke();
        }

        private void OnRestartClicked()
        {
            RestartRequested?.Invoke();
        }

        private void OnResumeClicked()
        {
            ResumeRequested?.Invoke();
        }

        private void OnMainMenuClicked()
        {
            GoToMenuRequested?.Invoke();
        }

        private static int ToKey(int x, int y)
        {
            return (y << 16) ^ x;
        }
    }
}
