using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UIElements;

namespace io.github.hatayama.uLoopMCP
{
    /// <summary>
    /// UI section for tool permissions and a virtualized per-tool enable list.
    /// The expensive list is allowed to stay unloaded while the foldout is collapsed.
    /// </summary>
    public class ToolSettingsSection
    {
        private const int ToolListRowHeight = 24;
        private const int InlineToolRowLimit = 40;

        private readonly Foldout _foldout;
        private readonly Toggle _allowThirdPartyToggle;
        private readonly Label _allowThirdPartyLabel;
        private readonly Button _securityLevelRestrictedButton;
        private readonly Button _securityLevelFullAccessButton;
        private readonly Label _securityLevelDescription;
        private readonly VisualElement _toolSettingsInfoContainer;
        private readonly VisualElement _toolListContainer;
        private readonly Label _toolListStatusLabel;
        private readonly ListView _toolListView;
        private readonly List<ToolListRowData> _toolListRows = new();
        private readonly Dictionary<string, Toggle> _togglesByToolName = new();
        private bool _allowThirdPartyTools = true;
        private bool _isRegistryAvailable;
        private bool _isUnavailableStateShown;
        private bool _isLoadingStateShown;
        private string _layoutSignature = string.Empty;

        public event Action<bool> OnFoldoutChanged;
        public event Action<string, bool> OnToolToggled;
        public event Action<bool> OnAllowThirdPartyChanged;
        public event Action<DynamicCodeSecurityLevel> OnSecurityLevelChanged;

        public ToolSettingsSection(VisualElement root)
        {
            _foldout = root.Q<Foldout>("tool-settings-foldout");
            _allowThirdPartyToggle = root.Q<Toggle>("allow-third-party-toggle");
            _allowThirdPartyLabel = root.Q<Label>("allow-third-party-label");
            _securityLevelRestrictedButton = root.Q<Button>("security-level-restricted-button");
            _securityLevelFullAccessButton = root.Q<Button>("security-level-full-access-button");
            _securityLevelDescription = root.Q<Label>("security-level-description");
            _toolSettingsInfoContainer = root.Q<VisualElement>("tool-settings-info-container");
            _toolListContainer = root.Q<VisualElement>("tool-list-container");
            Debug.Assert(_toolListContainer != null, "tool-list-container must not be null");

            _toolListStatusLabel = CreateToolListStatusLabel();
            _toolListView = CreateToolListView();
            _toolListContainer.Add(_toolListStatusLabel);
            _toolListContainer.Add(_toolListView);
            ClearToolList();

            Label cliReferenceLink = root.Q<Label>("cli-reference-link");
            if (cliReferenceLink != null)
            {
                cliReferenceLink.RegisterCallback<ClickEvent>(_ => Application.OpenURL(McpUIConstants.CLI_COMMAND_REFERENCE_URL));
            }

            SetupBindings();
        }

        private void SetupBindings()
        {
            _foldout.RegisterValueChangedCallback(evt => OnFoldoutChanged?.Invoke(evt.newValue));

            _allowThirdPartyToggle.RegisterValueChangedCallback(evt =>
            {
                evt.StopPropagation();
                OnAllowThirdPartyChanged?.Invoke(evt.newValue);
            });

            _allowThirdPartyLabel.RegisterCallback<ClickEvent>(evt =>
            {
                evt.StopPropagation();
                bool newValue = !_allowThirdPartyToggle.value;
                _allowThirdPartyToggle.SetValueWithoutNotify(newValue);
                OnAllowThirdPartyChanged?.Invoke(newValue);
            });

            _securityLevelRestrictedButton.clicked += () => UpdateSecurityLevel(DynamicCodeSecurityLevel.Restricted);
            _securityLevelFullAccessButton.clicked += () => UpdateSecurityLevel(DynamicCodeSecurityLevel.FullAccess);
        }

        public void Update(ToolSettingsSectionData data)
        {
            _allowThirdPartyTools = data.AllowThirdPartyTools;

            ViewDataBinder.UpdateFoldout(_foldout, data.ShowToolSettings);
            ViewDataBinder.UpdateToggle(_allowThirdPartyToggle, data.AllowThirdPartyTools);
            UpdateSecurityLevelSelection(data.DynamicCodeSecurityLevel);
            UpdateSecurityLevelDescription(data.DynamicCodeSecurityLevel);

            if (!data.ShowToolSettings)
            {
                ClearToolList();
                return;
            }

            if (!data.HasToolListData)
            {
                UpdateDeferredState();
                return;
            }

            if (!data.IsRegistryAvailable)
            {
                UpdateUnavailableState();
                return;
            }

            UpdateToolList(data);
        }

        public void UpdateSingleToggle(string toolName, bool enabled)
        {
            for (int i = 0; i < _toolListRows.Count; i++)
            {
                ToolListRowData row = _toolListRows[i];
                if (row.IsHeader || row.ToolName != toolName)
                {
                    continue;
                }

                row.IsEnabled = enabled;
                break;
            }

            if (_togglesByToolName.TryGetValue(toolName, out Toggle toggle))
            {
                toggle.SetValueWithoutNotify(enabled);
            }

            RefreshToolListView();
        }

        private void UpdateDeferredState()
        {
            if (_toolListRows.Count > 0 || _isUnavailableStateShown)
            {
                UpdateThirdPartyGroupState(_allowThirdPartyTools);
                return;
            }

            UpdateLoadingState();
        }

        private void UpdateLoadingState()
        {
            _toolListRows.Clear();
            _togglesByToolName.Clear();
            _layoutSignature = string.Empty;

            SetToolListStatus("Loading tools...");
            ViewDataBinder.SetVisible(_toolListView, false);
            SetToolSettingsInfoVisible(false);

            _isRegistryAvailable = false;
            _isUnavailableStateShown = false;
            _isLoadingStateShown = true;
        }

        private void UpdateUnavailableState()
        {
            _toolListRows.Clear();
            _togglesByToolName.Clear();
            _layoutSignature = string.Empty;

            SetToolListStatus("Tool registry not yet initialized. Start the server first.");
            ViewDataBinder.SetVisible(_toolListView, false);
            SetToolSettingsInfoVisible(false);

            _isRegistryAvailable = false;
            _isUnavailableStateShown = true;
            _isLoadingStateShown = false;
        }

        private void ClearToolList()
        {
            _toolListRows.Clear();
            _togglesByToolName.Clear();
            _layoutSignature = string.Empty;

            ViewDataBinder.SetVisible(_toolListStatusLabel, false);
            ViewDataBinder.SetVisible(_toolListView, false);
            SetToolSettingsInfoVisible(false);
            RefreshToolListView();

            _isRegistryAvailable = false;
            _isUnavailableStateShown = false;
            _isLoadingStateShown = false;
        }

        private void SetToolListStatus(string text)
        {
            _toolListStatusLabel.text = text;
            ViewDataBinder.SetVisible(_toolListStatusLabel, true);
        }

        private void SetToolSettingsInfoVisible(bool visible)
        {
            if (_toolSettingsInfoContainer == null)
            {
                return;
            }

            ViewDataBinder.SetVisible(_toolSettingsInfoContainer, visible);
        }

        private void UpdateToolList(ToolSettingsSectionData data)
        {
            string layoutSignature = CreateLayoutSignature(data);
            bool shouldRebuild = !_isRegistryAvailable
                || _isUnavailableStateShown
                || _isLoadingStateShown
                || _layoutSignature != layoutSignature;

            if (shouldRebuild)
            {
                Rebuild(data);
                _layoutSignature = layoutSignature;
            }
            else
            {
                UpdateToggleStates(data.BuiltInTools);
                UpdateToggleStates(data.ThirdPartyTools);
            }

            ViewDataBinder.SetVisible(_toolListStatusLabel, false);
            ViewDataBinder.SetVisible(_toolListView, true);
            SetToolSettingsInfoVisible(true);
            UpdateThirdPartyGroupState(data.AllowThirdPartyTools);

            _isRegistryAvailable = true;
            _isUnavailableStateShown = false;
            _isLoadingStateShown = false;
        }

        private void UpdateSecurityLevelDescription(DynamicCodeSecurityLevel currentLevel)
        {
            string description = currentLevel switch
            {
                DynamicCodeSecurityLevel.Restricted => "Dangerous APIs blocked (recommended)",
                DynamicCodeSecurityLevel.FullAccess => "All APIs available (use with caution)",
                _ => "Unknown level"
            };

            _securityLevelDescription.text = description;
            _securityLevelDescription.RemoveFromClassList("mcp-security-level-description--warning");

            if (currentLevel == DynamicCodeSecurityLevel.FullAccess)
            {
                _securityLevelDescription.AddToClassList("mcp-security-level-description--warning");
            }
        }

        private void UpdateSecurityLevel(DynamicCodeSecurityLevel newLevel)
        {
            UpdateSecurityLevelSelection(newLevel);
            UpdateSecurityLevelDescription(newLevel);
            OnSecurityLevelChanged?.Invoke(newLevel);
        }

        private void UpdateSecurityLevelSelection(DynamicCodeSecurityLevel currentLevel)
        {
            bool isRestricted = currentLevel == DynamicCodeSecurityLevel.Restricted;
            bool isFullAccess = currentLevel == DynamicCodeSecurityLevel.FullAccess;

            ViewDataBinder.ToggleClass(_securityLevelRestrictedButton, "mcp-segmented-control__button--active", isRestricted);
            ViewDataBinder.ToggleClass(_securityLevelRestrictedButton, "mcp-segmented-control__button--warning-active", false);
            ViewDataBinder.ToggleClass(_securityLevelFullAccessButton, "mcp-segmented-control__button--active", isFullAccess);
            ViewDataBinder.ToggleClass(_securityLevelFullAccessButton, "mcp-segmented-control__button--warning-active", isFullAccess);
        }

        private void Rebuild(ToolSettingsSectionData data)
        {
            _toolListRows.Clear();
            _togglesByToolName.Clear();

            if (data.BuiltInTools.Length > 0)
            {
                _toolListRows.Add(ToolListRowData.CreateHeader("Built-in Tools", false));
                AddToolRows(data.BuiltInTools);
            }

            if (data.ThirdPartyTools.Length > 0)
            {
                _toolListRows.Add(ToolListRowData.CreateHeader("Third Party Tools", true));
                AddToolRows(data.ThirdPartyTools);
            }

            UpdateToolListHeight();
            RefreshToolListView();
        }

        private void AddToolRows(IReadOnlyList<ToolToggleItem> items)
        {
            for (int i = 0; i < items.Count; i++)
            {
                ToolToggleItem item = items[i];
                _toolListRows.Add(ToolListRowData.CreateTool(item));
            }
        }

        private void UpdateToggleStates(IReadOnlyList<ToolToggleItem> items)
        {
            for (int i = 0; i < items.Count; i++)
            {
                ToolToggleItem item = items[i];
                UpdateToggleState(item.ToolName, item.IsEnabled);
            }
        }

        private void UpdateToggleState(string toolName, bool isEnabled)
        {
            for (int i = 0; i < _toolListRows.Count; i++)
            {
                ToolListRowData row = _toolListRows[i];
                if (row.IsHeader || row.ToolName != toolName)
                {
                    continue;
                }

                row.IsEnabled = isEnabled;
                return;
            }
        }

        private static string CreateLayoutSignature(ToolSettingsSectionData data)
        {
            StringBuilder builder = new StringBuilder();
            AppendGroupSignature(builder, data.BuiltInTools, "B");
            AppendGroupSignature(builder, data.ThirdPartyTools, "T");
            return builder.ToString();
        }

        private static void AppendGroupSignature(StringBuilder builder, IReadOnlyList<ToolToggleItem> items, string group)
        {
            builder.Append(group);
            builder.Append(':');

            for (int i = 0; i < items.Count; i++)
            {
                ToolToggleItem item = items[i];
                builder.Append(item.ToolName);
                builder.Append('|');
            }

            builder.Append(';');
        }

        private void UpdateThirdPartyGroupState(bool allowThirdPartyTools)
        {
            _allowThirdPartyTools = allowThirdPartyTools;
            RefreshToolListView();
        }

        private static Label CreateToolListStatusLabel()
        {
            Label label = new Label();
            label.name = "tool-list-status-label";
            label.AddToClassList("mcp-tool-registry-unavailable");
            return label;
        }

        private ListView CreateToolListView()
        {
            ListView listView = new ListView();
            listView.name = "tool-list-view";
            listView.AddToClassList("mcp-tool-list-view");
            listView.fixedItemHeight = ToolListRowHeight;
            listView.virtualizationMethod = CollectionVirtualizationMethod.FixedHeight;
            listView.selectionType = SelectionType.None;
            listView.itemsSource = _toolListRows;
            listView.makeItem = CreateToolListRowElement;
            listView.bindItem = BindToolListRowElement;
            listView.unbindItem = UnbindToolListRowElement;
            return listView;
        }

        private static VisualElement CreateToolListRowElement()
        {
            VisualElement row = new VisualElement();
            row.AddToClassList("mcp-tool-toggle-row");
            row.AddToClassList("mcp-tool-list-row");

            Toggle toggle = new Toggle();
            toggle.name = "tool-list-row-toggle";
            toggle.AddToClassList("mcp-tool-toggle-row__toggle");
            toggle.RegisterValueChangedCallback(evt =>
            {
                evt.StopPropagation();

                if (row.userData is not ToolListRowData item || item.IsHeader)
                {
                    return;
                }

                item.Owner?.OnToolToggled?.Invoke(item.ToolName, evt.newValue);
            });

            Label label = new Label();
            label.name = "tool-list-row-label";
            label.AddToClassList("mcp-tool-toggle-row__label");
            label.RegisterCallback<ClickEvent>(evt =>
            {
                evt.StopPropagation();

                if (row.userData is not ToolListRowData item || item.IsHeader || !item.CanToggle)
                {
                    return;
                }

                Toggle rowToggle = row.Q<Toggle>("tool-list-row-toggle");
                bool newValue = !rowToggle.value;
                rowToggle.SetValueWithoutNotify(newValue);
                item.Owner?.OnToolToggled?.Invoke(item.ToolName, newValue);
            });

            row.Add(toggle);
            row.Add(label);
            return row;
        }

        private void BindToolListRowElement(VisualElement row, int index)
        {
            Debug.Assert(index >= 0 && index < _toolListRows.Count, "tool list index must be valid");

            ToolListRowData item = _toolListRows[index];
            item.Owner = this;
            item.CanToggle = !item.IsThirdParty || _allowThirdPartyTools;
            row.userData = item;

            Toggle toggle = row.Q<Toggle>("tool-list-row-toggle");
            Label label = row.Q<Label>("tool-list-row-label");
            Debug.Assert(toggle != null, "tool-list-row-toggle must not be null");
            Debug.Assert(label != null, "tool-list-row-label must not be null");

            ResetRowClasses(row, label);

            if (item.IsHeader)
            {
                BindHeaderRow(row, toggle, label, item);
                return;
            }

            BindToolRow(row, toggle, label, item);
        }

        private void BindHeaderRow(VisualElement row, Toggle toggle, Label label, ToolListRowData item)
        {
            ViewDataBinder.SetVisible(toggle, false);
            label.text = item.Label;
            label.tooltip = string.Empty;
            row.SetEnabled(true);
            row.AddToClassList("mcp-tool-list-row--header");
            label.AddToClassList("mcp-tool-group-header");
            ViewDataBinder.ToggleClass(row, "mcp-tool-list-row--disabled", item.IsThirdParty && !_allowThirdPartyTools);
        }

        private void BindToolRow(VisualElement row, Toggle toggle, Label label, ToolListRowData item)
        {
            ViewDataBinder.SetVisible(toggle, true);
            toggle.SetValueWithoutNotify(item.IsEnabled);
            label.text = item.ToolName;
            label.tooltip = item.Description;

            row.SetEnabled(item.CanToggle);
            ViewDataBinder.ToggleClass(row, "mcp-tool-list-row--disabled", !item.CanToggle);
            _togglesByToolName[item.ToolName] = toggle;
        }

        private static void ResetRowClasses(VisualElement row, Label label)
        {
            row.RemoveFromClassList("mcp-tool-list-row--header");
            row.RemoveFromClassList("mcp-tool-list-row--disabled");
            label.RemoveFromClassList("mcp-tool-group-header");
        }

        private void UnbindToolListRowElement(VisualElement row, int index)
        {
            if (row.userData is ToolListRowData item && !item.IsHeader)
            {
                _togglesByToolName.Remove(item.ToolName);
            }

            row.userData = null;
        }

        private void RefreshToolListView()
        {
            _togglesByToolName.Clear();
            _toolListView.RefreshItems();
        }

        private void UpdateToolListHeight()
        {
            int visibleRows = Math.Min(_toolListRows.Count, InlineToolRowLimit);
            if (visibleRows <= 0)
            {
                visibleRows = 1;
            }

            _toolListView.style.height = (visibleRows * ToolListRowHeight) + 2;
        }

        private sealed class ToolListRowData
        {
            public readonly bool IsHeader;
            public readonly string ToolName;
            public readonly string Label;
            public readonly string Description;
            public readonly bool IsThirdParty;
            public bool IsEnabled;
            public bool CanToggle = true;
            public ToolSettingsSection Owner;

            private ToolListRowData(
                bool isHeader,
                string toolName,
                string label,
                string description,
                bool isEnabled,
                bool isThirdParty)
            {
                IsHeader = isHeader;
                ToolName = toolName;
                Label = label;
                Description = description;
                IsEnabled = isEnabled;
                IsThirdParty = isThirdParty;
            }

            public static ToolListRowData CreateHeader(string label, bool isThirdParty)
            {
                return new ToolListRowData(
                    true,
                    string.Empty,
                    label,
                    string.Empty,
                    true,
                    isThirdParty);
            }

            public static ToolListRowData CreateTool(ToolToggleItem item)
            {
                return new ToolListRowData(
                    false,
                    item.ToolName,
                    item.ToolName,
                    item.Description,
                    item.IsEnabled,
                    item.IsThirdParty);
            }
        }
    }
}
