using System;
using Minesweeper.Core;
using Minesweeper.Config;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

namespace Minesweeper.Presentation.Views
{
    public sealed class CellView : MonoBehaviour, IPointerClickHandler, IPointerDownHandler, IPointerUpHandler
    {
        [SerializeField] private Button button;
        [SerializeField] private Image backgroundImage;
        [SerializeField] private RectTransform contentRoot;
        [SerializeField] private Image contentImage;
        [SerializeField] private TMP_Text label;

        private int x;
        private int y;
        private bool inputEnabled = true;
        private bool isOpened;

        public event Action<int, int> OpenRequested;
        public event Action<int, int> FlagRequested;
        public event Action PressStarted;
        public event Action PressEnded;

        public void Bind(Button button, Image backgroundImage, RectTransform contentRoot, Image contentImage, TMP_Text label)
        {
            this.button = button;
            this.backgroundImage = backgroundImage;
            this.contentRoot = contentRoot;
            this.contentImage = contentImage;
            this.label = label;
        }

        public void Initialize(int x, int y)
        {
            this.x = x;
            this.y = y;
            gameObject.name = $"bt_{x}_{y}";
        }

        public void SetInputEnabled(bool enabled)
        {
            inputEnabled = enabled;
            if (button != null)
            {
                button.interactable = enabled && !isOpened;
            }
        }

        public void Render(BoardCellData cell, MinesweeperUiConfig config, float pixelsPerUnitMultiplier, float contentPadding, bool revealUnflaggedMines)
        {
            isOpened = cell.IsOpened;
            var revealMine = revealUnflaggedMines && cell.IsMine && !cell.IsFlagged;

            if (contentRoot != null)
            {
                contentRoot.offsetMin = new Vector2(contentPadding, contentPadding);
                contentRoot.offsetMax = new Vector2(-contentPadding, -contentPadding);
            }

            if (backgroundImage != null)
            {
                backgroundImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
                backgroundImage.color = cell.IsOpened || revealMine ? config.OpenedCellColor : config.ClosedCellColor;
            }

            if (contentImage != null)
            {
                contentImage.pixelsPerUnitMultiplier = pixelsPerUnitMultiplier;
            }

            RenderContent(cell, config, revealMine);

            if (button != null)
            {
                button.interactable = inputEnabled && !cell.IsOpened;
            }
        }

        private void RenderContent(BoardCellData cell, MinesweeperUiConfig config, bool revealMine)
        {
            var showFlag = cell.IsFlagged && !cell.IsOpened;
            var showMine = (cell.IsOpened && cell.IsMine) || revealMine;
            var showMineAsText = showMine && config.MineSprite == null;
            var showNumber = cell.IsOpened && !cell.IsMine && cell.NeighborMines > 0;

            if (contentImage != null)
            {
                SetActiveIfChanged(contentImage.gameObject, showFlag || (showMine && !showMineAsText));
                if (showFlag)
                {
                    SetSpriteIfChanged(contentImage, config.FlagSprite);
                }
                else if (showMine)
                {
                    SetSpriteIfChanged(contentImage, config.MineSprite);
                }

                contentImage.color = Color.white;
            }

            if (label != null)
            {
                SetActiveIfChanged(label.gameObject, showNumber || showMineAsText);
                SetTextIfChanged(label, showMineAsText ? "M" : showNumber ? cell.NeighborMines.ToString() : string.Empty);
                label.color = config.GetNumberTextColor(cell.NeighborMines);
            }
        }

        private static void SetActiveIfChanged(GameObject target, bool active)
        {
            if (target.activeSelf != active)
            {
                target.SetActive(active);
            }
        }

        private static void SetSpriteIfChanged(Image image, Sprite sprite)
        {
            if (image.sprite != sprite)
            {
                image.sprite = sprite;
            }
        }

        private static void SetTextIfChanged(TMP_Text text, string value)
        {
            if (text.text != value)
            {
                text.text = value;
            }
        }

        public void OnPointerClick(PointerEventData eventData)
        {
            if (!inputEnabled)
            {
                return;
            }

            if (eventData.button == PointerEventData.InputButton.Left)
            {
                OpenRequested?.Invoke(x, y);
            }
            else if (eventData.button == PointerEventData.InputButton.Right)
            {
                FlagRequested?.Invoke(x, y);
            }
        }

        public void OnPointerDown(PointerEventData eventData)
        {
            if (inputEnabled && eventData.button == PointerEventData.InputButton.Left)
            {
                PressStarted?.Invoke();
            }
        }

        public void OnPointerUp(PointerEventData eventData)
        {
            if (eventData.button == PointerEventData.InputButton.Left)
            {
                PressEnded?.Invoke();
            }
        }
    }
}
