// Package circuitbreaker provides protection against cascading failures.
package circuitbreaker

import (
	"sync"
	"time"

	"github.com/orchard9/rdev/internal/metrics"
)

// Registry manages named circuit breakers.
type Registry struct {
	mu       sync.RWMutex
	breakers map[string]*CircuitBreaker
	configs  map[string]Config
}

// NewRegistry creates a new circuit breaker registry.
func NewRegistry() *Registry {
	return &Registry{
		breakers: make(map[string]*CircuitBreaker),
		configs:  make(map[string]Config),
	}
}

// Get returns the circuit breaker for the given name, creating one if needed.
func (r *Registry) Get(name string) *CircuitBreaker {
	r.mu.RLock()
	cb, ok := r.breakers[name]
	r.mu.RUnlock()
	if ok {
		return cb
	}

	r.mu.Lock()
	defer r.mu.Unlock()

	// Double-check after acquiring write lock
	if cb, ok = r.breakers[name]; ok {
		return cb
	}

	// Use custom config if set, otherwise use defaults
	cfg, ok := r.configs[name]
	if !ok {
		cfg = DefaultConfig()
	}

	// Ensure name is set for callbacks
	cfg.Name = name

	// Set up metrics callback if not already configured
	if cfg.OnStateChange == nil {
		cfg.OnStateChange = defaultStateChangeCallback
	}

	cb = New(cfg)
	r.breakers[name] = cb
	return cb
}

// Configure sets the configuration for a named circuit breaker.
// Must be called before Get() for the configuration to take effect.
func (r *Registry) Configure(name string, cfg Config) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.configs[name] = cfg
}

// StatusEntry contains the status of a single circuit breaker.
type StatusEntry struct {
	Name        string     `json:"name"`
	State       string     `json:"state"`
	Failures    int        `json:"failures"`
	LastFailure *time.Time `json:"last_failure,omitempty"`
}

// AllStatus returns the status of all registered circuit breakers.
func (r *Registry) AllStatus() []StatusEntry {
	r.mu.RLock()
	defer r.mu.RUnlock()

	entries := make([]StatusEntry, 0, len(r.breakers))
	for name, cb := range r.breakers {
		stats := cb.Stats()
		entry := StatusEntry{
			Name:     name,
			State:    stats.State.String(),
			Failures: stats.Failures,
		}
		if !stats.LastFailure.IsZero() {
			t := stats.LastFailure.UTC()
			entry.LastFailure = &t
		}
		entries = append(entries, entry)
	}
	return entries
}

// Reset resets a specific circuit breaker by name.
func (r *Registry) Reset(name string) bool {
	r.mu.RLock()
	cb, ok := r.breakers[name]
	r.mu.RUnlock()
	if !ok {
		return false
	}
	cb.Reset()
	return true
}

// ResetAll resets all circuit breakers.
func (r *Registry) ResetAll() {
	r.mu.RLock()
	defer r.mu.RUnlock()
	for _, cb := range r.breakers {
		cb.Reset()
	}
}

// GlobalRegistry is the default global circuit breaker registry.
var GlobalRegistry = NewRegistry()

// defaultStateChangeCallback updates Prometheus metrics when circuit breaker state changes.
func defaultStateChangeCallback(name string, from, to State) {
	// State values: 0=closed, 1=half-open, 2=open
	metrics.SetCircuitBreakerState(name, int(to))
}

// Known circuit breaker names for external systems.
const (
	NameWoodpecker = "woodpecker"
	NameGitea      = "gitea"
	NameRegistry   = "registry"
	NameCloudflare = "cloudflare"
)

func init() {
	// Configure default circuit breakers with appropriate settings
	GlobalRegistry.Configure(NameWoodpecker, Config{
		FailureThreshold: 3,
		ResetTimeout:     30 * time.Second,
		HalfOpenRequests: 1,
	})
	GlobalRegistry.Configure(NameGitea, Config{
		FailureThreshold: 3,
		ResetTimeout:     30 * time.Second,
		HalfOpenRequests: 1,
	})
	GlobalRegistry.Configure(NameRegistry, Config{
		FailureThreshold: 5,
		ResetTimeout:     60 * time.Second,
		HalfOpenRequests: 1,
	})
	GlobalRegistry.Configure(NameCloudflare, Config{
		FailureThreshold: 5,
		ResetTimeout:     60 * time.Second,
		HalfOpenRequests: 1,
	})
}
