using UnityEngine;

namespace JebAI
{
    /// <summary>
    /// A temporary world-space speech bubble that tracks a Kerbal's part or vessel.
    /// </summary>
    public class SpeechBubble : MonoBehaviour
    {
        private SpeechBubbleManager _owner;
        private string _kerbalName;
        private string _message;
        private Transform _anchor;
        private Vector3 _fallbackWorldPosition;
        private float _expiresAt;
        private float _shownAt;

        private GUIStyle _boxStyle;
        private GUIStyle _nameStyle;
        private GUIStyle _textStyle;
        private bool _stylesReady;

        private const float Width = 260f;
        private const float Padding = 10f;

        public string KerbalName => _kerbalName;

        public void Initialize(
            SpeechBubbleManager owner,
            string kerbalName,
            string message,
            Transform anchor,
            Vector3 fallbackWorldPosition,
            float duration)
        {
            _owner = owner;
            _kerbalName = kerbalName;
            Refresh(message, anchor, fallbackWorldPosition, duration);
        }

        public void Refresh(
            string message,
            Transform anchor,
            Vector3 fallbackWorldPosition,
            float duration)
        {
            _message = message ?? string.Empty;
            _anchor = anchor;
            _fallbackWorldPosition = fallbackWorldPosition;
            _shownAt = Time.time;
            _expiresAt = Time.time + duration;
        }

        void Update()
        {
            if (Time.time >= _expiresAt)
                Destroy(gameObject);
        }

        void OnDestroy()
        {
            _owner?.Release(this);
        }

        void OnGUI()
        {
            if (string.IsNullOrEmpty(_message)) return;
            if (!_stylesReady) InitStyles();

            Camera cam = FlightCamera.fetch != null ? FlightCamera.fetch.mainCamera : Camera.main;
            if (cam == null) return;

            Vector3 worldPosition = ResolveBubbleWorldPosition();
            Vector3 screenPoint = cam.WorldToScreenPoint(worldPosition);
            if (screenPoint.z <= 0f) return;

            float bodyWidth = Width - (Padding * 2f);
            float textHeight = _textStyle.CalcHeight(new GUIContent(_message), bodyWidth);
            float bubbleHeight = Padding + 18f + 6f + textHeight + Padding;
            float x = Mathf.Clamp(screenPoint.x - (Width * 0.5f), 8f, Screen.width - Width - 8f);
            float y = Mathf.Clamp(Screen.height - screenPoint.y - bubbleHeight - 18f, 8f, Screen.height - bubbleHeight - 8f);
            float timeLeft = _expiresAt - Time.time;
            float fadeIn = Mathf.Clamp01((Time.time - _shownAt) / 0.12f);
            float fadeOut = timeLeft < 1f ? Mathf.Clamp01(timeLeft) : 1f;
            float alpha = fadeIn * fadeOut;

            Color previous = GUI.color;
            GUI.color = new Color(1f, 1f, 1f, alpha);

            GUI.Box(new Rect(x, y, Width, bubbleHeight), GUIContent.none, _boxStyle);
            GUI.Label(new Rect(x + Padding, y + Padding, bodyWidth, 18f), _kerbalName, _nameStyle);
            GUI.Label(new Rect(x + Padding, y + Padding + 24f, bodyWidth, textHeight), _message, _textStyle);

            GUI.color = previous;
        }

        private Vector3 ResolveBubbleWorldPosition()
        {
            if (_anchor != null && TryGetAnchorBounds(_anchor, out Bounds bounds))
            {
                float verticalOffset = Mathf.Max(0.45f, bounds.size.y * 0.18f);
                return new Vector3(
                    bounds.center.x,
                    bounds.max.y + verticalOffset,
                    bounds.center.z);
            }

            if (_anchor != null)
                return _anchor.position + ResolveUp(_anchor) * 0.55f;

            return _fallbackWorldPosition + Vector3.up * 2.4f;
        }

        private void InitStyles()
        {
            _boxStyle = new GUIStyle(GUI.skin.box);
            _boxStyle.normal.background = MakeSolidTexture(new Color(0.06f, 0.08f, 0.11f, 0.92f));
            _boxStyle.padding = new RectOffset(0, 0, 0, 0);

            _nameStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = 12,
                fontStyle = FontStyle.Bold,
            };
            _nameStyle.normal.textColor = new Color(0.64f, 0.90f, 0.98f);

            _textStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = 12,
                wordWrap = true,
                alignment = TextAnchor.UpperLeft,
            };
            _textStyle.normal.textColor = new Color(0.94f, 0.94f, 0.96f);

            _stylesReady = true;
        }

        private static Texture2D MakeSolidTexture(Color color)
        {
            var tex = new Texture2D(1, 1, TextureFormat.RGBA32, false);
            tex.SetPixel(0, 0, color);
            tex.Apply();
            return tex;
        }

        private static bool TryGetAnchorBounds(Transform anchor, out Bounds bounds)
        {
            Renderer[] renderers = anchor.GetComponentsInChildren<Renderer>(true);
            if (renderers != null && renderers.Length > 0)
            {
                bounds = renderers[0].bounds;
                for (int i = 1; i < renderers.Length; i++)
                    bounds.Encapsulate(renderers[i].bounds);
                return true;
            }

            bounds = default;
            return false;
        }

        private static Vector3 ResolveUp(Transform anchor)
        {
            if (anchor == null) return Vector3.up;

            Vector3 up = anchor.up;
            if (up.sqrMagnitude < 0.1f)
                return Vector3.up;

            return up.normalized;
        }
    }
}
