using System;
using MCPForUnity.Editor.Constants;
using UnityEditor;

namespace MCPForUnity.Editor.Services
{
    /// <summary>
    /// Centralized cache for frequently-read EditorPrefs values.
    /// Reduces scattered EditorPrefs.Get* calls and provides change notification.
    ///
    /// Usage:
    ///   var config = EditorConfigurationCache.Instance;
    ///   if (config.UseHttpTransport) { ... }
    ///   config.OnConfigurationChanged += (key) => { /* refresh UI */ };
    /// </summary>
    public class EditorConfigurationCache
    {
        private static EditorConfigurationCache _instance;
        private static readonly object _lock = new object();

        /// <summary>
        /// Singleton instance. Thread-safe lazy initialization.
        /// </summary>
        public static EditorConfigurationCache Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (_lock)
                    {
                        if (_instance == null)
                        {
                            _instance = new EditorConfigurationCache();
                        }
                    }
                }
                return _instance;
            }
        }

        /// <summary>
        /// Event fired when any cached configuration value changes.
        /// The string parameter is the EditorPrefKeys constant name that changed.
        /// </summary>
        public event Action<string> OnConfigurationChanged;

        // Cached values - most frequently read
        private bool _useHttpTransport;
        private bool _debugLogs;
        private bool _devModeForceServerRefresh;
        private string _uvxPathOverride;
        private string _gitUrlOverride;
        private string _httpBaseUrl;
        private string _httpRemoteBaseUrl;
        private string _claudeCliPathOverride;
        private string _httpTransportScope;
        private int _unitySocketPort;

        /// <summary>
        /// Whether to use HTTP transport (true) or Stdio transport (false).
        /// Default: true
        /// </summary>
        public bool UseHttpTransport => _useHttpTransport;

        /// <summary>
        /// Whether debug logging is enabled.
        /// Default: false
        /// </summary>
        public bool DebugLogs => _debugLogs;

        /// <summary>
        /// Whether to force server refresh in dev mode (--no-cache --refresh).
        /// Default: false
        /// </summary>
        public bool DevModeForceServerRefresh => _devModeForceServerRefresh;

        /// <summary>
        /// Custom path override for uvx executable.
        /// Default: empty string (auto-detect)
        /// </summary>
        public string UvxPathOverride => _uvxPathOverride;

        /// <summary>
        /// Custom Git URL override for server installation.
        /// Default: empty string (use default)
        /// </summary>
        public string GitUrlOverride => _gitUrlOverride;

        /// <summary>
        /// HTTP base URL for the local MCP server.
        /// Default: empty string
        /// </summary>
        public string HttpBaseUrl => _httpBaseUrl;

        /// <summary>
        /// HTTP base URL for the remote-hosted MCP server.
        /// Default: empty string
        /// </summary>
        public string HttpRemoteBaseUrl => _httpRemoteBaseUrl;

        /// <summary>
        /// Custom path override for Claude CLI executable.
        /// Default: empty string (auto-detect)
        /// </summary>
        public string ClaudeCliPathOverride => _claudeCliPathOverride;

        /// <summary>
        /// HTTP transport scope: "local" or "remote".
        /// Default: empty string
        /// </summary>
        public string HttpTransportScope => _httpTransportScope;

        /// <summary>
        /// Unity socket port for Stdio transport.
        /// Default: 0 (auto-assign)
        /// </summary>
        public int UnitySocketPort => _unitySocketPort;

        private EditorConfigurationCache()
        {
            Refresh();
        }

        /// <summary>
        /// Refresh all cached values from EditorPrefs.
        /// Call this after bulk EditorPrefs changes or domain reload.
        /// </summary>
        public void Refresh()
        {
            _useHttpTransport = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
            _debugLogs = EditorPrefs.GetBool(EditorPrefKeys.DebugLogs, false);
            _devModeForceServerRefresh = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false);
            _uvxPathOverride = EditorPrefs.GetString(EditorPrefKeys.UvxPathOverride, string.Empty);
            _gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, string.Empty);
            _httpBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpBaseUrl, string.Empty);
            _httpRemoteBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpRemoteBaseUrl, string.Empty);
            _claudeCliPathOverride = EditorPrefs.GetString(EditorPrefKeys.ClaudeCliPathOverride, string.Empty);
            _httpTransportScope = EditorPrefs.GetString(EditorPrefKeys.HttpTransportScope, string.Empty);
            _unitySocketPort = EditorPrefs.GetInt(EditorPrefKeys.UnitySocketPort, 0);
        }

        /// <summary>
        /// Set UseHttpTransport and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetUseHttpTransport(bool value)
        {
            if (_useHttpTransport != value)
            {
                _useHttpTransport = value;
                EditorPrefs.SetBool(EditorPrefKeys.UseHttpTransport, value);
                OnConfigurationChanged?.Invoke(nameof(UseHttpTransport));
            }
        }

        /// <summary>
        /// Set DebugLogs and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetDebugLogs(bool value)
        {
            if (_debugLogs != value)
            {
                _debugLogs = value;
                EditorPrefs.SetBool(EditorPrefKeys.DebugLogs, value);
                OnConfigurationChanged?.Invoke(nameof(DebugLogs));
            }
        }

        /// <summary>
        /// Set DevModeForceServerRefresh and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetDevModeForceServerRefresh(bool value)
        {
            if (_devModeForceServerRefresh != value)
            {
                _devModeForceServerRefresh = value;
                EditorPrefs.SetBool(EditorPrefKeys.DevModeForceServerRefresh, value);
                OnConfigurationChanged?.Invoke(nameof(DevModeForceServerRefresh));
            }
        }

        /// <summary>
        /// Set UvxPathOverride and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetUvxPathOverride(string value)
        {
            value = value ?? string.Empty;
            if (_uvxPathOverride != value)
            {
                _uvxPathOverride = value;
                EditorPrefs.SetString(EditorPrefKeys.UvxPathOverride, value);
                OnConfigurationChanged?.Invoke(nameof(UvxPathOverride));
            }
        }

        /// <summary>
        /// Set GitUrlOverride and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetGitUrlOverride(string value)
        {
            value = value ?? string.Empty;
            if (_gitUrlOverride != value)
            {
                _gitUrlOverride = value;
                EditorPrefs.SetString(EditorPrefKeys.GitUrlOverride, value);
                OnConfigurationChanged?.Invoke(nameof(GitUrlOverride));
            }
        }

        /// <summary>
        /// Set HttpBaseUrl and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetHttpBaseUrl(string value)
        {
            value = value ?? string.Empty;
            if (_httpBaseUrl != value)
            {
                _httpBaseUrl = value;
                EditorPrefs.SetString(EditorPrefKeys.HttpBaseUrl, value);
                OnConfigurationChanged?.Invoke(nameof(HttpBaseUrl));
            }
        }

        /// <summary>
        /// Set HttpRemoteBaseUrl and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetHttpRemoteBaseUrl(string value)
        {
            value = value ?? string.Empty;
            if (_httpRemoteBaseUrl != value)
            {
                _httpRemoteBaseUrl = value;
                EditorPrefs.SetString(EditorPrefKeys.HttpRemoteBaseUrl, value);
                OnConfigurationChanged?.Invoke(nameof(HttpRemoteBaseUrl));
            }
        }

        /// <summary>
        /// Set ClaudeCliPathOverride and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetClaudeCliPathOverride(string value)
        {
            value = value ?? string.Empty;
            if (_claudeCliPathOverride != value)
            {
                _claudeCliPathOverride = value;
                EditorPrefs.SetString(EditorPrefKeys.ClaudeCliPathOverride, value);
                OnConfigurationChanged?.Invoke(nameof(ClaudeCliPathOverride));
            }
        }

        /// <summary>
        /// Set HttpTransportScope and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetHttpTransportScope(string value)
        {
            value = value ?? string.Empty;
            if (_httpTransportScope != value)
            {
                _httpTransportScope = value;
                EditorPrefs.SetString(EditorPrefKeys.HttpTransportScope, value);
                OnConfigurationChanged?.Invoke(nameof(HttpTransportScope));
            }
        }

        /// <summary>
        /// Set UnitySocketPort and update cache + EditorPrefs atomically.
        /// </summary>
        public void SetUnitySocketPort(int value)
        {
            if (_unitySocketPort != value)
            {
                _unitySocketPort = value;
                EditorPrefs.SetInt(EditorPrefKeys.UnitySocketPort, value);
                OnConfigurationChanged?.Invoke(nameof(UnitySocketPort));
            }
        }

        /// <summary>
        /// Force refresh of a single cached value from EditorPrefs.
        /// Useful when external code modifies EditorPrefs directly.
        /// </summary>
        public void InvalidateKey(string keyName)
        {
            switch (keyName)
            {
                case nameof(UseHttpTransport):
                    _useHttpTransport = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
                    break;
                case nameof(DebugLogs):
                    _debugLogs = EditorPrefs.GetBool(EditorPrefKeys.DebugLogs, false);
                    break;
                case nameof(DevModeForceServerRefresh):
                    _devModeForceServerRefresh = EditorPrefs.GetBool(EditorPrefKeys.DevModeForceServerRefresh, false);
                    break;
                case nameof(UvxPathOverride):
                    _uvxPathOverride = EditorPrefs.GetString(EditorPrefKeys.UvxPathOverride, string.Empty);
                    break;
                case nameof(GitUrlOverride):
                    _gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, string.Empty);
                    break;
                case nameof(HttpBaseUrl):
                    _httpBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpBaseUrl, string.Empty);
                    break;
                case nameof(HttpRemoteBaseUrl):
                    _httpRemoteBaseUrl = EditorPrefs.GetString(EditorPrefKeys.HttpRemoteBaseUrl, string.Empty);
                    break;
                case nameof(ClaudeCliPathOverride):
                    _claudeCliPathOverride = EditorPrefs.GetString(EditorPrefKeys.ClaudeCliPathOverride, string.Empty);
                    break;
                case nameof(HttpTransportScope):
                    _httpTransportScope = EditorPrefs.GetString(EditorPrefKeys.HttpTransportScope, string.Empty);
                    break;
                case nameof(UnitySocketPort):
                    _unitySocketPort = EditorPrefs.GetInt(EditorPrefKeys.UnitySocketPort, 0);
                    break;
            }
            OnConfigurationChanged?.Invoke(keyName);
        }
    }
}
