using System.Collections.Generic;
using UnityEngine;

namespace JebAI
{
    /// <summary>
    /// Draggable chat window for direct conversation with a specific Kerbal.
    ///
    /// Features:
    ///   - Per-Kerbal conversation history (switching Kerbals clears and restarts)
    ///   - Current mood displayed in the title bar
    ///   - Full history sent to the OpenAI-compatible local server so the Kerbal remembers the conversation
    ///   - Live flight context injected into each system prompt
    ///   - Replies mirrored to speech bubbles so other players can see reactions
    ///
    /// Opened via the toolbar crew list or a crew-part right-click action after
    /// selecting which Kerbal to talk to.
    /// </summary>
    public class ChatWindow : MonoBehaviour
    {
        private static readonly Rect DefaultWindowRect = new Rect(60f, 120f, 540f, 440f);
        private const float MinWindowWidth = 420f;
        private const float MinWindowHeight = 280f;
        private const float ResizeHandleSize = 18f;

        public static ChatWindow Instance { get; private set; }

        // ── State ─────────────────────────────────────────────────────────────

        private bool            _visible      = false;
        private ProtoCrewMember _kerbal       = null;
        private string          _inputText    = "";
        private Vector2         _scrollPos;
        private bool            _waitingReply = false;
        private bool            _needsScroll  = false;
        private bool            _focusInputNextFrame = false;
        private bool            _resizing = false;
        private Vector2         _resizeStartMouse;
        private Rect            _resizeStartRect;

        // Display messages (includes system notices shown to player)
        private readonly List<(string role, string text)> _display =
            new List<(string, string)>();

        // API history (user/assistant only — sent to the local OpenAI-compatible server)
        private readonly List<ChatMessage> _apiHistory = new List<ChatMessage>();

        // Window layout
        private Rect    _windowRect = DefaultWindowRect;
        private const int WinId = 0x1EB3A1;

        // Styles
        private GUIStyle _winStyle, _userStyle, _aiStyle, _sysStyle, _inputStyle, _btnStyle, _moodStyle;
        private bool     _stylesReady;

        // ── Unity lifecycle ────────────────────────────────────────────────────

        void Awake()
        {
            if (Instance != null && Instance != this) { Destroy(gameObject); return; }
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }

        void OnGUI()
        {
            if (!_visible || _kerbal == null) return;
            if (!_stylesReady) InitStyles();
            EnsureWindowRect();

            // Title includes Kerbal name + current mood
            string mood  = KerbalMoodTracker.Get(_kerbal.name).Describe();
            string title = $"  JebAI — {_kerbal.name}  [{mood}]";

            _windowRect = GUI.Window(
                WinId, _windowRect, DrawWindow, title, _winStyle);
        }

        // ── Public API ────────────────────────────────────────────────────────

        public void OpenFor(ProtoCrewMember crew)
        {
            bool sameKerbal = _kerbal?.name == crew.name;
            _kerbal  = crew;
            _visible = true;
            _focusInputNextFrame = true;

            if (_windowRect.width < MinWindowWidth || _windowRect.height < MinWindowHeight)
                _windowRect = DefaultWindowRect;

            EnsureWindowRect();

            if (!sameKerbal)
            {
                _display.Clear();
                _apiHistory.Clear();
                _inputText = "";
                AddSysMessage($"— Talking to {crew.name} —");
                AddSysMessage($"Mood: {KerbalMoodTracker.Get(crew.name).Describe()}");
            }
        }

        public void Toggle()  { if (_kerbal != null) _visible = !_visible; }
        public void Close()   => _visible = false;

        // ── Window rendering ──────────────────────────────────────────────────

        private void DrawWindow(int id)
        {
            if (!_stylesReady) InitStyles();
            HandleResizeEvents();

            // Close button
            if (GUI.Button(new Rect(_windowRect.width - 28, 4, 22, 18), "✕", _btnStyle))
                Close();

            float bodyTop = 26f;
            float inputH  = 36f;
            float scrollH = _windowRect.height - bodyTop - inputH - 14f;

            // ── Scroll area ────────────────────────────────────────────────────
            _scrollPos = GUI.BeginScrollView(
                new Rect(0, bodyTop, _windowRect.width, scrollH),
                _scrollPos,
                new Rect(0, 0, _windowRect.width - 18f, CalcContentHeight()));

            float y = 6f;
            foreach (var (role, text) in _display)
            {
                var (label, style) = FormatMessage(role, text);
                float h = style.CalcHeight(new GUIContent(label), _windowRect.width - 28f);
                GUI.Label(new Rect(8f, y, _windowRect.width - 28f, h), label, style);
                y += h + 5f;
            }

            if (_waitingReply)
            {
                string thinking = $"{_kerbal?.name} is thinking...";
                GUI.Label(new Rect(8f, y, 320f, 22f), thinking, _sysStyle);
            }

            GUI.EndScrollView();

            // Auto-scroll when new messages arrive
            if (_needsScroll)
            {
                _scrollPos.y = float.MaxValue;
                _needsScroll = false;
            }

            // ── Input row ──────────────────────────────────────────────────────
            float inputY = _windowRect.height - inputH - 4f;

            GUI.SetNextControlName("JebAI_ChatInput");
            _inputText = GUI.TextField(
                new Rect(8f, inputY, _windowRect.width - 90f, 28f),
                _inputText, _inputStyle);

            if (_focusInputNextFrame && Event.current.type == EventType.Repaint)
            {
                GUI.FocusControl("JebAI_ChatInput");
                _focusInputNextFrame = false;
            }

            bool enter = Event.current.type == EventType.KeyDown
                      && (Event.current.keyCode == KeyCode.Return
                          || Event.current.keyCode == KeyCode.KeypadEnter)
                      && GUI.GetNameOfFocusedControl() == "JebAI_ChatInput";

            bool clicked = GUI.Button(
                new Rect(_windowRect.width - 80f, inputY, 72f, 28f), "Send", _btnStyle);

            if ((clicked || enter) && !_waitingReply && _inputText.Trim().Length > 0)
            {
                string msg = _inputText.Trim();
                _inputText = "";
                SendUserMessage(msg);
                GUI.FocusControl("JebAI_ChatInput");
            }

            DrawResizeHandle();
            GUI.DragWindow(new Rect(0, 0, _windowRect.width, 24f));
        }

        // ── Messaging ─────────────────────────────────────────────────────────

        private void SendUserMessage(string text)
        {
            ProtoCrewMember targetKerbal = _kerbal;
            if (targetKerbal == null)
                return;

            _display.Add(("user", text));
            _apiHistory.Add(new ChatMessage { Role = "user", Content = text });
            _waitingReply = true;
            _needsScroll  = true;

            Vessel contextVessel = FindKerbalVessel(targetKerbal.name) ?? FlightGlobals.ActiveVessel;
            JebAI.Instance?.PrepareKerbalForInteraction(targetKerbal, contextVessel);

            string ctx    = ContextBuilder.BuildFlightContext(contextVessel);
            string system = KerbalPersona.BuildSystemPrompt(targetKerbal, InteractionType.Chat)
                          + $"\n\nFlight background for flavor only. Do not summarize it back unless it truly matters:\n{ctx}";

            if (JebAIMcpLayer.Instance == null)
            {
                AddSysMessage("[MCP unavailable.]");
                _waitingReply = false;
                _needsScroll  = true;
                return;
            }

            JebAIMcpLayer.Instance.RequestCompletion(
                system,
                _apiHistory,
                null,
                JebAISettings.Instance.MaxTokensChat,
                $"Chat:{targetKerbal.name}",
                result =>
                {
                    if (result != null && result.Success)
                    {
                        string compact = OpenAICompatClient.ClampReplyForInteraction(result.Reply, InteractionType.Chat);
                        if (!string.IsNullOrWhiteSpace(compact))
                        {
                            bool sameActiveChat = _kerbal != null && _kerbal.name == targetKerbal.name;
                            if (sameActiveChat)
                            {
                                _display.Add(("assistant", compact));
                                _apiHistory.Add(new ChatMessage { Role = "assistant", Content = compact });
                            }

                            KerbalMemory.AddRecentOutput(targetKerbal.name, InteractionType.Chat, compact);
                            SpeechBubbleManager.Instance?.Show(targetKerbal, compact);
                        }
                        else
                        {
                            AddSysMessage("[Model returned blank visible text.]");
                        }
                    }
                    else
                    {
                        string error = result?.Error ?? "MCP request failed.";
                        if (ShouldUseSyntheticFallback(error))
                        {
                            string synthetic = "Comms cut out for a beat. I'm still here.";
                            bool sameActiveChat = _kerbal != null && _kerbal.name == targetKerbal.name;
                            if (sameActiveChat)
                            {
                                _display.Add(("assistant", synthetic));
                                _apiHistory.Add(new ChatMessage { Role = "assistant", Content = synthetic });
                            }

                            KerbalMemory.AddRecentOutput(targetKerbal.name, InteractionType.Chat, synthetic);
                            SpeechBubbleManager.Instance?.Show(targetKerbal, synthetic);
                        }
                        else
                        {
                            AddSysMessage($"[No response - {error}]");
                        }
                    }

                    _waitingReply = false;
                    _needsScroll  = true;
                });
        }

        private void AddSysMessage(string text) => _display.Add(("system", text));

        // ── Helpers ────────────────────────────────────────────────────────────

        private static Vessel FindKerbalVessel(string kerbalName)
        {
            if (string.IsNullOrEmpty(kerbalName) || FlightGlobals.VesselsLoaded == null)
                return null;

            foreach (Vessel vessel in FlightGlobals.VesselsLoaded)
            {
                if (vessel == null) continue;
                List<ProtoCrewMember> crew = vessel.GetVesselCrew();
                if (crew == null) continue;

                for (int i = 0; i < crew.Count; i++)
                {
                    ProtoCrewMember member = crew[i];
                    if (member != null && member.name == kerbalName)
                        return vessel;
                }
            }

            return null;
        }

        private static bool ShouldUseSyntheticFallback(string error)
        {
            if (string.IsNullOrWhiteSpace(error)) return false;
            string lower = error.ToLowerInvariant();
            return lower.Contains("reasoning content")
                || lower.Contains("empty choices")
                || lower.Contains("blank message content")
                || lower.Contains("parse failed");
        }

        private (string label, GUIStyle style) FormatMessage(string role, string text)
        {
            return role switch
            {
                "user"      => ($"You:  {text}",                    _userStyle),
                "assistant" => ($"{_kerbal?.name}:  {text}",        _aiStyle),
                _           => (text,                                _sysStyle),
            };
        }

        private float CalcContentHeight()
        {
            float h = 6f;
            float w = _windowRect.width - 28f;
            foreach (var (role, text) in _display)
            {
                var (label, style) = FormatMessage(role, text);
                h += style.CalcHeight(new GUIContent(label), w) + 5f;
            }
            if (_waitingReply) h += 26f;
            return Mathf.Max(h, _windowRect.height - 80f);
        }

        // ── Style init ────────────────────────────────────────────────────────

        private void InitStyles()
        {
            _winStyle = new GUIStyle(GUI.skin.window)
            {
                fontSize  = 12,
                fontStyle = FontStyle.Bold,
            };

            _userStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = 13,
                wordWrap = true,
                normal   = { textColor = new Color(0.75f, 1.00f, 0.75f) }
            };

            _aiStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize = 13,
                wordWrap = true,
                normal   = { textColor = new Color(0.80f, 0.88f, 1.00f) }
            };

            _sysStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize  = 11,
                wordWrap  = true,
                fontStyle = FontStyle.Italic,
                normal    = { textColor = new Color(0.50f, 0.50f, 0.58f) }
            };

            _moodStyle = new GUIStyle(GUI.skin.label)
            {
                fontSize  = 11,
                fontStyle = FontStyle.Italic,
                normal    = { textColor = new Color(0.80f, 0.70f, 0.40f) }
            };

            _inputStyle = new GUIStyle(GUI.skin.textField) { fontSize = 13 };

            _btnStyle = new GUIStyle(GUI.skin.button)
            {
                fontSize  = 12,
                fontStyle = FontStyle.Bold,
            };

            _stylesReady = true;
        }

        private void DrawResizeHandle()
        {
            Rect handleRect = GetResizeHandleRect();
            GUI.Box(handleRect, "=", _btnStyle);
        }

        private void HandleResizeEvents()
        {
            Event evt = Event.current;
            Rect handleRect = GetResizeHandleRect();

            if (evt.type == EventType.MouseDown && evt.button == 0 && handleRect.Contains(evt.mousePosition))
            {
                _resizing = true;
                _resizeStartMouse = evt.mousePosition;
                _resizeStartRect = _windowRect;
                evt.Use();
                return;
            }

            if (_resizing && evt.type == EventType.MouseDrag)
            {
                Vector2 delta = evt.mousePosition - _resizeStartMouse;
                _windowRect.width = Mathf.Max(MinWindowWidth, _resizeStartRect.width + delta.x);
                _windowRect.height = Mathf.Max(MinWindowHeight, _resizeStartRect.height + delta.y);
                EnsureWindowRect();
                evt.Use();
                return;
            }

            if (_resizing && (evt.type == EventType.MouseUp || evt.rawType == EventType.MouseUp))
            {
                _resizing = false;
                evt.Use();
            }
        }

        private Rect GetResizeHandleRect()
        {
            return new Rect(
                _windowRect.width - ResizeHandleSize - 6f,
                _windowRect.height - ResizeHandleSize - 6f,
                ResizeHandleSize,
                ResizeHandleSize);
        }

        private void EnsureWindowRect()
        {
            float screenWidth = Mathf.Max(Screen.width, MinWindowWidth + 16f);
            float screenHeight = Mathf.Max(Screen.height, MinWindowHeight + 16f);

            _windowRect.width = Mathf.Clamp(_windowRect.width, MinWindowWidth, screenWidth - 8f);
            _windowRect.height = Mathf.Clamp(_windowRect.height, MinWindowHeight, screenHeight - 8f);
            _windowRect.x = Mathf.Clamp(_windowRect.x, 4f, screenWidth - _windowRect.width - 4f);
            _windowRect.y = Mathf.Clamp(_windowRect.y, 4f, screenHeight - _windowRect.height - 4f);
        }
    }
}
