using System;
using Net.Eagle0.Eagle.Api;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

namespace eagle {
    /// <summary>
    /// Provides game state information for the connection status UI.
    /// Implement this interface to show game-specific status when connected.
    /// </summary>
    public interface IGameStateProvider {
        /// <summary>Server-reported game status. Null if no status received yet.</summary>
        ServerGameStatus ServerStatus { get; }

        /// <summary>True if a command was submitted and we're awaiting response (for >
        /// 500ms).</summary>
        bool IsProcessingCommand { get; }
    }

    /// <summary>
    /// Shardok-side counterpart to <see cref="IGameStateProvider"/>. The two protos and providers
    /// are kept separate on purpose so the strategic and tactical layers can evolve independently.
    /// </summary>
    public interface IShardokGameStateProvider {
        /// <summary>Server-reported Shardok battle status. Null if no status received
        /// yet.</summary>
        ShardokServerStatus ShardokServerStatus { get; }
    }

    /// <summary>
    /// Simple UI component to display connection status and reconnection countdown.
    /// Attach to a TextMeshProUGUI component to display status.
    /// </summary>
    public class ConnectionStatusUI : MonoBehaviour {
        [Header("UI References")]
        public TextMeshProUGUI statusText;

        private PersistentClientConnection _connection;
        private IGameStateProvider _gameStateProvider;
        private IShardokGameStateProvider _shardokGameStateProvider;
        private string _environmentName;

        [Tooltip("Optional text component to display connected environment (prod/qa)")]
        public TextMeshProUGUI environmentIndicatorText;

        [Tooltip("Optional button to force immediate reconnection when server is down")]
        public Button retryButton;

        // Update interval in seconds
        private const float UpdateInterval = 0.5f;
        private float _timeSinceLastUpdate = 0f;

        void Start() {
            if (statusText == null) {
                Debug.LogError("ConnectionStatusUI: statusText reference not assigned");
                enabled = false;
                return;
            }

            if (retryButton != null) {
                retryButton.onClick.AddListener(OnRetryClicked);
                retryButton.gameObject.SetActive(false);
            }
        }

        /// <summary>
        /// Set the connection to monitor. Call this after creating the connection.
        /// </summary>
        public void SetConnection(PersistentClientConnection connection) {
            _connection = connection;
        }

        /// <summary>
        /// Set the game state provider for showing game-specific status.
        /// Call this when entering a game, clear it when leaving.
        /// </summary>
        public void SetGameStateProvider(IGameStateProvider provider) {
            _gameStateProvider = provider;
        }

        /// <summary>
        /// Set the Shardok game state provider for showing battle-specific status.
        /// Call this when entering a Shardok battle, pass null when leaving so the Eagle
        /// strategic status takes over again.
        /// </summary>
        public void SetShardokGameStateProvider(IShardokGameStateProvider provider) {
            _shardokGameStateProvider = provider;
        }

        /// <summary>
        /// Set the environment name to display (e.g., "prod." or "qa.").
        /// Pass null to clear the indicator.
        /// </summary>
        public void SetEnvironment(string environmentName) {
            _environmentName = environmentName;
            UpdateEnvironmentIndicator();
        }

        void Update() {
            if (_connection == null || statusText == null) { return; }

            _timeSinceLastUpdate += Time.deltaTime;
            if (_timeSinceLastUpdate < UpdateInterval) { return; }

            _timeSinceLastUpdate = 0f;
            UpdateDisplay();
        }

        private void UpdateDisplay() {
            // Authentication failures are actionable by the player and must not be hidden by
            // a circuit breaker that may have opened during an earlier network failure.
            var state = _connection.CurrentState;
            if (state == ConnectionState.RefreshingAuthentication) {
                SetRetryButtonVisible(false);
                statusText.text = "<color=yellow>●</color> Refreshing session...";
                return;
            }
            if (state == ConnectionState.AuthenticationRequired) {
                SetRetryButtonVisible(false);
                statusText.text =
                        "<color=red>●</color> Session expired. Return to lobby to sign in again.";
                return;
            }

            // Check circuit breaker state first - it takes precedence
            var circuitState = _connection.CircuitBreaker.CurrentState;
            if (circuitState == ConnectionCircuitBreaker.State.Open) {
                SetRetryButtonVisible(true);
                var nextTest = _connection.CircuitBreaker.NextTestAttempt;
                if (nextTest.HasValue) {
                    var timeUntilTest = nextTest.Value - DateTime.UtcNow;
                    if (timeUntilTest.TotalSeconds > 0) {
                        int seconds = Math.Min(10, (int)Math.Ceiling(timeUntilTest.TotalSeconds));
                        statusText.text = $"<color=red>●</color> Server down. Retry in {seconds}s";
                        return;
                    }
                }
                statusText.text = "<color=red>●</color> Server unavailable";
                return;
            } else if (circuitState == ConnectionCircuitBreaker.State.HalfOpen) {
                SetRetryButtonVisible(false);
                statusText.text = "<color=yellow>●</color> Testing connection...";
                return;
            }

            // Normal connection state display
            var nextAttempt = _connection.NextReconnectAttempt;

            // Show retry button if we're counting down to a reconnect attempt
            bool isCountingDown = state == ConnectionState.Reconnecting && nextAttempt.HasValue &&
                                  (nextAttempt.Value - DateTime.UtcNow).TotalSeconds > 0;
            SetRetryButtonVisible(isCountingDown);

            string displayText = state switch {
                ConnectionState.Connected => GetConnectedStatusText(),
                ConnectionState.Connecting => "<color=yellow>●</color> Connecting...",
                ConnectionState.Disconnected => "<color=red>●</color> Disconnected",
                ConnectionState.Reconnecting => GetReconnectingText(nextAttempt),
                ConnectionState.SubscriptionPending => "<color=yellow>●</color> Subscribing...",
                _ => "<color=gray>●</color> Unknown"
            };

            statusText.text = displayText;
        }

        private string GetConnectedStatusText() {
            // While a Shardok battle is on screen, the tactical status takes precedence over the
            // strategic one.
            if (_shardokGameStateProvider != null) { return GetShardokConnectedStatusText(); }

            // If no game state provider, just show "Connected"
            if (_gameStateProvider == null) { return "<color=green>●</color> Connected"; }

            // Processing takes priority (client knows it submitted a command)
            if (_gameStateProvider.IsProcessingCommand) {
                return "<color=green>●</color> Processing...";
            }

            // Use server-reported status
            var serverStatus = _gameStateProvider.ServerStatus;
            if (serverStatus == null) {
                // No server status yet - waiting for first response
                return "<color=yellow>●</color> Waiting for server...";
            }

            return serverStatus.Status switch {
                ServerGameStatus.Types.Status.YourTurn => "<color=green>●</color> Your turn",
                ServerGameStatus.Types.Status.WaitingForPlayers =>
                        "<color=green>●</color> Waiting for other players",
                ServerGameStatus.Types.Status.GeneratingText =>
                        "<color=green>●</color> Generating...",
                ServerGameStatus.Types.Status.ProcessingAction =>
                        "<color=green>●</color> Processing...",
                ServerGameStatus.Types.Status.BattleInProgress =>
                        "<color=green>●</color> Battle in progress",
                _ => "<color=green>●</color> Connected"
            };
        }

        private string GetShardokConnectedStatusText() {
            var status = _shardokGameStateProvider.ShardokServerStatus;
            if (status == null) { return "<color=yellow>●</color> Waiting for server..."; }

            return status.Status switch { ShardokServerStatus.Types.Status.YourTurn =>
                                                  "<color=green>●</color> Your turn",
                                          ShardokServerStatus.Types.Status.WaitingForAi =>
                                                  "<color=green>●</color> Waiting for AI",
                                          ShardokServerStatus.Types.Status.WaitingForHumanPlayer =>
                                                  "<color=green>●</color> Waiting for other player",
                                          ShardokServerStatus.Types.Status.Processing =>
                                                  "<color=green>●</color> Processing...",
                                          _ => "<color=green>●</color> Connected" };
        }

        private string GetReconnectingText(DateTime? nextAttempt) {
            if (!nextAttempt.HasValue) { return "<color=yellow>●</color> Reconnecting..."; }

            var timeUntilRetry = nextAttempt.Value - DateTime.UtcNow;
            if (timeUntilRetry.TotalSeconds <= 0) {
                return "<color=yellow>●</color> Reconnecting...";
            }

            int secondsRemaining = Math.Min(10, (int)Math.Ceiling(timeUntilRetry.TotalSeconds));
            return $"<color=orange>●</color> Retry in {secondsRemaining}s";
        }

        private void SetRetryButtonVisible(bool visible) {
            if (retryButton != null) { retryButton.gameObject.SetActive(visible); }
        }

        private void OnRetryClicked() {
            if (_connection != null) { _connection.ForceReconnect(); }
        }

        private void UpdateEnvironmentIndicator() {
            if (environmentIndicatorText == null) return;

            // Only show environment indicator in Unity Editor
            if (!Application.isEditor) {
                environmentIndicatorText.text = "";
                return;
            }

            if (string.IsNullOrEmpty(_environmentName)) {
                environmentIndicatorText.text = "";
                return;
            }

            // Color prod green, qa yellow
            environmentIndicatorText.text =
                    _environmentName switch { "prod." => $"<color=green>{_environmentName}</color>",
                                              "qa." => $"<color=yellow>{_environmentName}</color>",
                                              _ => _environmentName };
        }
    }
}
