using System;
using System.Collections;
using Core.Domain;
using Core.Ports;
using KBCore.Refs;
using UnityEngine;

namespace Infrastructure.Unity
{
    [RequireComponent(typeof(MeshRenderer), typeof(Rigidbody))]
    public class TileViewAdapter : MonoBehaviour, ITileView
    {
        private Tile _linkedTile;

        public string TileId { get; private set; }

        [Header("Visuals Settings")]
        [SerializeField] private Color stableColor = new Color(0.33725490196078434f, 0.5058823529411764f, 0.9529411764705882f); // Dark Grey
        [SerializeField] private Color warningColor = new Color(1f, 0.1803921568627451f, 0.38823529411764707f); // Neon Red
        [SerializeField] private Color fragileColor = new Color(0.08627450980392157f, 0.12941176470588237f, 0.24313725490196078f, 0.5f); // Cyan/Glass
        [SerializeField] private float colorSpeed = 2f;

        [Self][SerializeField] private Rigidbody rb;
        [Self][SerializeField] private MeshRenderer meshRenderer;
        [Self] [SerializeField] private Collider col;

        private MaterialPropertyBlock _propBlock;
        private static readonly int ColorProperty = Shader.PropertyToID("_BaseColor");
        private static readonly int EmissionColorProperty = Shader.PropertyToID("_EmissionColor");
        
        private Action<TileViewAdapter> _onReturnToPool;
        private float _targetAlpha = 1f;
        
        public MeshRenderer MeshRenderer => meshRenderer;

        private void Awake()
        {
            _propBlock = new MaterialPropertyBlock();

            rb.isKinematic = true;
            rb.useGravity = false;
        }

        public void Initialize(Tile tile)
        {
            Initialize(tile, null);
        }

        public void Initialize(Tile tile, Action<TileViewAdapter> onReturnToPool = null)
        {
            _linkedTile = tile;
            TileId = tile.Id;
            _onReturnToPool = onReturnToPool;

            // Reset physics
            rb.isKinematic = true;
            rb.useGravity = false;

            if (_linkedTile.Type == TileType.Fragile)
            {
                SetColor(fragileColor);
            }
            else
            {
                SetColor(ThemeManager.CurrentTheme.StableColor);
            }

            _linkedTile.OnStateChanged += OnTileStateChanged;
        }

        private void OnTileStateChanged(Tile tile)
        {
            SetVisualState(tile.CurrentState);
        }

        public void SetVisualState(TileState state)
        {
            switch (state)
            {
                case TileState.Stable:
                    StartCoroutine(AnimateColor(ThemeManager.CurrentTheme.StableColor));
                    break;
                case TileState.Warning:
                    StartCoroutine(AnimateColor(ThemeManager.CurrentTheme.WarningColor));
                    break;
                case TileState.Falling:
                    DropPhysics();
                    break;
                case TileState.Destroyed:
                    Dispose();
                    break;
                default:
                    throw new ArgumentOutOfRangeException(nameof(state), state, null);
            }
        }

        public void DropPhysics()
        {
            rb.isKinematic = false;
            rb.useGravity = true;
            col.enabled = false;
        }

        public void Dispose()
        {
            StartCoroutine(ShrinkAndDestroy());
        }

        public void StepOn()
        {
            _linkedTile?.StepOn();
        }

        public void PulseEmission(float intensity)
        {
            LeanTween.cancel(gameObject);
            
            transform.localScale = Vector3.one; 
            LeanTween.scale(gameObject, Vector3.one * 1.05f, 0.15f).setEasePunch();

            meshRenderer.GetPropertyBlock(_propBlock);
            var baseColor = _propBlock.GetColor(ColorProperty);
    
            var flashColor = baseColor * intensity;

            LeanTween.value(gameObject, (Color col) => 
                {
                    meshRenderer.GetPropertyBlock(_propBlock);
                    _propBlock.SetColor(EmissionColorProperty, col);
                    meshRenderer.SetPropertyBlock(_propBlock);
                }, flashColor, Color.black, 0.4f)
                .setEaseOutQuad();
        }
        
        public void SetAlpha(float alpha)
        {
            _targetAlpha = alpha;
    
            meshRenderer.GetPropertyBlock(_propBlock);
            var currentColor = _propBlock.GetColor(ColorProperty);
            currentColor.a = _targetAlpha;
            _propBlock.SetColor(ColorProperty, currentColor);
            meshRenderer.SetPropertyBlock(_propBlock);
        }
        
        private void SetColor(Color color)
        {
            meshRenderer.GetPropertyBlock(_propBlock);
            
            color.a = _targetAlpha;
            
            _propBlock.SetColor(ColorProperty, color);
            meshRenderer.SetPropertyBlock(_propBlock);
        }

        private IEnumerator AnimateColor(Color targetColor)
        {
            meshRenderer.GetPropertyBlock(_propBlock);
            var startColor = _propBlock.GetColor(ColorProperty);
            var t = 0f;

            while (t < 1f)
            {
                t += Time.deltaTime * colorSpeed;
                SetColor(Color.Lerp(startColor, targetColor, t));
                yield return null;
            }
        }

        private IEnumerator ShrinkAndDestroy()
        {
            var t = 0f;
            var startScale = transform.localScale;
            while (t < 0.4f)
            {
                t += Time.deltaTime;
                transform.localScale = Vector3.Lerp(startScale, Vector3.zero, t);
                yield return null;
            }

            if (_onReturnToPool != null)
            {
                _onReturnToPool(this);
            }
            else
            {
                Destroy(gameObject);
            }
        }

        private void OnDestroy()
        {
            if (_linkedTile != null)
            {
                _linkedTile.OnStateChanged -= OnTileStateChanged;
            }
        }
    }
}