package traefikoidc

import (
	"context"
	"fmt"
	"time"
)

// TokenResilienceConfig centralizes resilience configuration for token operations
type TokenResilienceConfig struct {
	MetadataCacheConfig   MetadataCacheResilienceConfig
	RetryConfig           RetryConfig
	CircuitBreakerConfig  CircuitBreakerConfig
	CircuitBreakerEnabled bool
	RetryEnabled          bool
}

// MetadataCacheResilienceConfig defines resilience settings for metadata cache
type MetadataCacheResilienceConfig struct {
	SecurityCriticalFields         []string
	InitialGracePeriod             time.Duration
	ExtendedGracePeriod            time.Duration
	MaxGracePeriod                 time.Duration
	SecurityCriticalMaxGracePeriod time.Duration
	EnableProgressiveGracePeriod   bool
}

// DefaultTokenResilienceConfig returns the default resilience configuration for token operations
func DefaultTokenResilienceConfig() TokenResilienceConfig {
	return TokenResilienceConfig{
		CircuitBreakerEnabled: true,
		CircuitBreakerConfig: CircuitBreakerConfig{
			MaxFailures:  3,
			Timeout:      30 * time.Second,
			ResetTimeout: 15 * time.Second,
		},
		RetryEnabled: true,
		RetryConfig: RetryConfig{
			MaxAttempts:   3,
			InitialDelay:  250 * time.Millisecond,
			MaxDelay:      2 * time.Second,
			BackoffFactor: 2.0,
			EnableJitter:  true,
			RetryableErrors: []string{
				"connection refused",
				"timeout",
				"temporary failure",
				"network unreachable",
				"connection reset",
				"no route to host",
			},
		},
		MetadataCacheConfig: DefaultMetadataCacheResilienceConfig(),
	}
}

// DefaultMetadataCacheResilienceConfig returns the default metadata cache resilience configuration
func DefaultMetadataCacheResilienceConfig() MetadataCacheResilienceConfig {
	return MetadataCacheResilienceConfig{
		EnableProgressiveGracePeriod:   true,
		InitialGracePeriod:             5 * time.Minute,
		ExtendedGracePeriod:            15 * time.Minute,
		MaxGracePeriod:                 30 * time.Minute,
		SecurityCriticalMaxGracePeriod: 15 * time.Minute, // Allan's security limit
		SecurityCriticalFields: []string{
			"jwks_uri",
			"authorization_endpoint",
			"token_endpoint",
			"revocation_endpoint",
			"end_session_endpoint",
		},
	}
}

// TokenResilienceManager coordinates resilience mechanisms for token operations
type TokenResilienceManager struct {
	errorRecoveryManager *ErrorRecoveryManager
	circuitBreaker       *CircuitBreaker
	retryExecutor        *RetryExecutor
	logger               *Logger
	config               TokenResilienceConfig
}

// NewTokenResilienceManager creates a new token resilience manager
func NewTokenResilienceManager(config TokenResilienceConfig, logger *Logger) *TokenResilienceManager {
	manager := &TokenResilienceManager{
		config: config,
		logger: logger,
	}

	// Initialize error recovery manager
	manager.errorRecoveryManager = NewErrorRecoveryManager(logger)

	// Initialize circuit breaker if enabled
	if config.CircuitBreakerEnabled {
		manager.circuitBreaker = NewCircuitBreaker(config.CircuitBreakerConfig, logger)
	}

	// Initialize retry executor if enabled
	if config.RetryEnabled {
		manager.retryExecutor = NewRetryExecutor(config.RetryConfig, logger)
	}

	return manager
}

// ExecuteTokenOperation executes a token operation with full resilience support
func (trm *TokenResilienceManager) ExecuteTokenOperation(ctx context.Context, operation string, fn func() error) error {
	if trm.logger != nil {
		trm.logger.Debugf("Executing token operation %s with resilience", operation)
	}

	// If no resilience mechanisms are enabled, execute directly
	if !trm.config.CircuitBreakerEnabled && !trm.config.RetryEnabled {
		return fn()
	}

	// Compose resilience mechanisms
	var finalOperation func() error = fn

	// Wrap with circuit breaker if enabled
	if trm.config.CircuitBreakerEnabled && trm.circuitBreaker != nil {
		originalOp := finalOperation
		finalOperation = func() error {
			return trm.circuitBreaker.ExecuteWithContext(ctx, originalOp)
		}
	}

	// Wrap with retry if enabled
	if trm.config.RetryEnabled && trm.retryExecutor != nil {
		originalOp := finalOperation
		finalOperation = func() error {
			return trm.retryExecutor.ExecuteWithContext(ctx, originalOp)
		}
	}

	err := finalOperation()

	if err != nil && trm.logger != nil {
		trm.logger.Errorf("Token operation %s failed after resilience mechanisms: %v", operation, err)
	} else if trm.logger != nil {
		trm.logger.Debugf("Token operation %s completed successfully", operation)
	}

	return err
}

// ExecuteTokenExchange executes token exchange with resilience
func (trm *TokenResilienceManager) ExecuteTokenExchange(ctx context.Context, t *TraefikOidc, grantType, codeOrToken, redirectURL, codeVerifier string) (*TokenResponse, error) {
	var result *TokenResponse
	var err error

	operation := fmt.Sprintf("token_exchange_%s", grantType)

	err = trm.ExecuteTokenOperation(ctx, operation, func() error {
		result, err = t.exchangeTokens(ctx, grantType, codeOrToken, redirectURL, codeVerifier)
		return err
	})

	return result, err
}

// ExecuteTokenRefresh executes token refresh with resilience
func (trm *TokenResilienceManager) ExecuteTokenRefresh(ctx context.Context, t *TraefikOidc, refreshToken string) (*TokenResponse, error) {
	var result *TokenResponse
	var err error

	err = trm.ExecuteTokenOperation(ctx, "token_refresh", func() error {
		// Call exchangeTokens directly to avoid recursion back to getNewTokenWithRefreshToken
		// which would call ExecuteTokenRefresh again, causing infinite loop (issue #67)
		result, err = t.exchangeTokens(ctx, "refresh_token", refreshToken, "", "")
		return err
	})

	return result, err
}

// GetMetrics returns metrics for all resilience mechanisms
func (trm *TokenResilienceManager) GetMetrics() map[string]interface{} {
	metrics := make(map[string]interface{})

	if trm.circuitBreaker != nil {
		metrics["circuit_breaker"] = trm.circuitBreaker.GetMetrics()
	}

	if trm.retryExecutor != nil {
		metrics["retry_executor"] = trm.retryExecutor.GetMetrics()
	}

	if trm.errorRecoveryManager != nil {
		recoveryMetrics := trm.errorRecoveryManager.GetRecoveryMetrics()
		metrics["error_recovery"] = recoveryMetrics
	}

	return metrics
}

// Reset resets all resilience mechanisms
func (trm *TokenResilienceManager) Reset() {
	if trm.circuitBreaker != nil {
		trm.circuitBreaker.Reset()
	}

	if trm.retryExecutor != nil {
		trm.retryExecutor.Reset()
	}

	if trm.logger != nil {
		trm.logger.Debugf("Token resilience manager has been reset")
	}
}

// IsSecurityCriticalField checks if a metadata field is security-critical
func (config MetadataCacheResilienceConfig) IsSecurityCriticalField(fieldName string) bool {
	for _, criticalField := range config.SecurityCriticalFields {
		if fieldName == criticalField {
			return true
		}
	}
	return false
}

// GetEffectiveMaxGracePeriod returns the effective maximum grace period for a field
// considering Allan's security limits
func (config MetadataCacheResilienceConfig) GetEffectiveMaxGracePeriod(fieldName string) time.Duration {
	if config.IsSecurityCriticalField(fieldName) {
		return config.SecurityCriticalMaxGracePeriod
	}
	return config.MaxGracePeriod
}
