using System;
using common;

namespace eagle {
    /// <summary>
    /// Circuit breaker pattern for connection failures.
    /// Prevents cascading failures by blocking connection attempts during server outages.
    /// </summary>
    public class ConnectionCircuitBreaker {
        public enum State {
            Closed,   // Normal operation, allowing connections
            Open,     // Too many failures, blocking connections
            HalfOpen  // Testing if service recovered
        }

        private const int FailureThreshold = 5;          // Open after 5 failures
        private const double OpenTimeoutSeconds = 60.0;  // Wait 60s before test
        private const double SuccessResetThreshold = 3;  // Close after 3 successes

        private State _state = State.Closed;
        private int _failureCount = 0;
        private int _successCount = 0;
        private DateTime? _openedAt = null;

        private readonly Logger _logger = Logger.GetLogger("ConnectionLogger");

        public State CurrentState {
            get {
                lock (this) { return _state; }
            }
        }

        public DateTime? NextTestAttempt {
            get {
                lock (this) {
                    if (_state == State.Open && _openedAt.HasValue) {
                        return _openedAt.Value.AddSeconds(OpenTimeoutSeconds);
                    }
                    return null;
                }
            }
        }

        /// <summary>
        /// Check if a connection attempt should be allowed.
        /// </summary>
        public bool ShouldAttemptConnection() {
            lock (this) {
                switch (_state) {
                    case State.Closed: return true;  // Normal operation

                    case State.HalfOpen: return true;  // Allow test attempt

                    case State.Open:
                        // Check if timeout has elapsed
                        if (_openedAt.HasValue) {
                            var elapsed = (DateTime.UtcNow - _openedAt.Value).TotalSeconds;
                            if (elapsed >= OpenTimeoutSeconds) {
                                // Transition to half-open for test
                                _state = State.HalfOpen;
                                _logger.LogLine(
                                        $"[CIRCUIT] OPEN → HALF_OPEN (testing after {elapsed:F1}s)");
                                return true;
                            }
                        }
                        return false;  // Still in open state, block connection
                }

                return false;
            }
        }

        /// <summary>
        /// Record a successful connection.
        /// </summary>
        public void RecordSuccess() {
            lock (this) {
                if (_state == State.HalfOpen) {
                    // Test succeeded, close circuit
                    _state = State.Closed;
                    _failureCount = 0;
                    _successCount = 0;
                    _openedAt = null;
                    _logger.LogLine($"[CIRCUIT] HALF_OPEN → CLOSED (test succeeded)");
                } else if (_state == State.Closed) {
                    // Normal success, increment counter
                    _successCount++;
                    if (_failureCount > 0) {
                        _failureCount = Math.Max(0, _failureCount - 1);  // Decay failures
                    }
                    if (_failureCount == 0 && _successCount >= SuccessResetThreshold) {
                        _successCount = 0;  // Reset success counter
                        _logger.LogLine($"[CIRCUIT] CLOSED (connection stable)");
                    }
                }
            }
        }

        /// <summary>
        /// Record a connection failure.
        /// </summary>
        public void RecordFailure() {
            lock (this) {
                _failureCount++;
                _successCount = 0;  // Reset success counter on any failure

                if (_state == State.HalfOpen) {
                    // Test attempt failed, reopen circuit
                    _state = State.Open;
                    _openedAt = DateTime.UtcNow;
                    _logger.LogLine(
                            $"[CIRCUIT] HALF_OPEN → OPEN (test failed, failures={_failureCount})");
                } else if (_failureCount >= FailureThreshold && _state == State.Closed) {
                    // Too many failures, open circuit
                    _state = State.Open;
                    _openedAt = DateTime.UtcNow;
                    _logger.LogLine(
                            $"[CIRCUIT] CLOSED → OPEN (failures={_failureCount}, threshold={FailureThreshold})");
                }
            }
        }

        /// <summary>
        /// Force the circuit breaker to allow an immediate reconnect attempt.
        /// Resets the state to HalfOpen so the next connection will be a test.
        /// </summary>
        public void ForceReconnect() {
            lock (this) {
                if (_state == State.Open) {
                    _state = State.HalfOpen;
                    _logger.LogLine("[CIRCUIT] OPEN → HALF_OPEN (forced by user)");
                }
            }
        }

        /// <summary>
        /// Get human-readable status for UI display.
        /// </summary>
        public string GetStatusMessage() {
            lock (this) {
                switch (_state) {
                    case State.Closed:
                        return _failureCount > 0
                                       ? $"Connection recovering ({_failureCount} recent failures)"
                                       : "Connection healthy";

                    case State.HalfOpen: return "Testing connection...";

                    case State.Open:
                        if (_openedAt.HasValue) {
                            var timeUntilTest = OpenTimeoutSeconds -
                                                (DateTime.UtcNow - _openedAt.Value).TotalSeconds;
                            if (timeUntilTest > 0) {
                                return $"Server unavailable. Retrying in {Math.Min(10, (int)timeUntilTest)}s";
                            }
                        }
                        return "Server unavailable. Testing...";

                    default: return "Unknown state";
                }
            }
        }
    }
}
