package com.flightticket.config;

import io.github.resilience4j.circuitbreaker.CircuitBreakerRegistry;
import io.github.resilience4j.retry.RetryRegistry;
import io.github.resilience4j.timelimiter.TimeLimiterRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;

/**
 * Resilience4j configuration for external API calls
 * 
 * Implements three resilience patterns:
 * 1. Circuit Breaker: Prevents cascade failures by opening circuit after threshold failures
 * 2. Retry: Automatically retries failed requests with exponential backoff
 * 3. Time Limiter: Limits execution time to prevent hanging requests
 * 
 * Requirements: 13.1, 13.4
 */
@Configuration
@Slf4j
public class ResilienceConfig {
    
    private final CircuitBreakerRegistry circuitBreakerRegistry;
    private final RetryRegistry retryRegistry;
    private final TimeLimiterRegistry timeLimiterRegistry;
    
    public ResilienceConfig(CircuitBreakerRegistry circuitBreakerRegistry,
                           RetryRegistry retryRegistry,
                           TimeLimiterRegistry timeLimiterRegistry) {
        this.circuitBreakerRegistry = circuitBreakerRegistry;
        this.retryRegistry = retryRegistry;
        this.timeLimiterRegistry = timeLimiterRegistry;
    }
    
    /**
     * Configure circuit breaker event listeners after application startup
     * 
     * Circuit Breaker States:
     * - CLOSED: Normal operation, requests pass through
     * - OPEN: Too many failures, requests are rejected immediately
     * - HALF_OPEN: Testing if service recovered, limited requests allowed
     */
    @EventListener(ApplicationReadyEvent.class)
    public void configureCircuitBreakerEventListeners() {
        // Amadeus circuit breaker listeners
        circuitBreakerRegistry.circuitBreaker("amadeus").getEventPublisher()
            .onStateTransition(event -> {
                log.warn("Circuit breaker state transition: {} -> {} for {}",
                    event.getStateTransition().getFromState(),
                    event.getStateTransition().getToState(),
                    event.getCircuitBreakerName());
            })
            .onError(event -> {
                log.error("Circuit breaker recorded error for {}: {}",
                    event.getCircuitBreakerName(),
                    event.getThrowable().getMessage());
            })
            .onSuccess(event -> {
                log.debug("Circuit breaker recorded success for {}",
                    event.getCircuitBreakerName());
            });
        
        // Expedia circuit breaker listeners (Requirements: 9.5)
        circuitBreakerRegistry.circuitBreaker("expedia").getEventPublisher()
            .onStateTransition(event -> {
                log.warn("Circuit breaker state transition: {} -> {} for {} at {} (reason: {})",
                    event.getStateTransition().getFromState(),
                    event.getStateTransition().getToState(),
                    event.getCircuitBreakerName(),
                    event.getCreationTime(),
                    event.getStateTransition().getToState().name());
            })
            .onError(event -> {
                log.error("Circuit breaker recorded error for {} at {}: {}",
                    event.getCircuitBreakerName(),
                    event.getCreationTime(),
                    event.getThrowable().getMessage());
            })
            .onSuccess(event -> {
                log.debug("Circuit breaker recorded success for {} at {}",
                    event.getCircuitBreakerName(),
                    event.getCreationTime());
            });
    }
    
    /**
     * Configure retry event listeners after application startup
     * 
     * Retry Strategy:
     * - Initial wait: 1 second
     * - Exponential backoff: 2x multiplier (1s, 2s, 4s)
     * - Max attempts: 3 (including initial call)
     */
    @EventListener(ApplicationReadyEvent.class)
    public void configureRetryEventListeners() {
        // Amadeus retry listeners
        retryRegistry.retry("amadeus").getEventPublisher()
            .onRetry(event -> {
                log.warn("Retry attempt {} for {} due to: {}",
                    event.getNumberOfRetryAttempts(),
                    event.getName(),
                    event.getLastThrowable().getMessage());
            })
            .onError(event -> {
                log.error("All retry attempts exhausted for {}: {}",
                    event.getName(),
                    event.getLastThrowable().getMessage());
            });
        
        // Expedia retry listeners (Requirements: 9.2)
        retryRegistry.retry("expedia").getEventPublisher()
            .onRetry(event -> {
                log.warn("Retry attempt {} for {} due to: {}",
                    event.getNumberOfRetryAttempts(),
                    event.getName(),
                    event.getLastThrowable().getMessage());
            })
            .onError(event -> {
                log.error("All retry attempts exhausted for {}: {}",
                    event.getName(),
                    event.getLastThrowable().getMessage());
            });
    }
    
    /**
     * Configure time limiter event listeners after application startup
     * 
     * Timeout Strategy:
     * - Maximum execution time: 10 seconds
     * - Cancel running future on timeout
     */
    @EventListener(ApplicationReadyEvent.class)
    public void configureTimeLimiterEventListeners() {
        // Amadeus time limiter listeners
        timeLimiterRegistry.timeLimiter("amadeus").getEventPublisher()
            .onTimeout(event -> {
                log.error("Time limiter timeout for {}: execution exceeded timeout duration",
                    event.getTimeLimiterName());
            })
            .onSuccess(event -> {
                log.debug("Time limiter success for {}",
                    event.getTimeLimiterName());
            });
        
        // Expedia time limiter listeners (Requirements: 9.3)
        timeLimiterRegistry.timeLimiter("expedia").getEventPublisher()
            .onTimeout(event -> {
                log.error("Time limiter timeout for {}: execution exceeded timeout duration",
                    event.getTimeLimiterName());
            })
            .onSuccess(event -> {
                log.debug("Time limiter success for {}",
                    event.getTimeLimiterName());
            });
    }
}
