<?php

declare(strict_types=1);

namespace App\Framework\CircuitBreaker\Examples;

use App\Framework\CircuitBreaker\CircuitBreakerConfig;
use App\Framework\CircuitBreaker\FailurePredicate\FailurePredicateFactory;
use App\Framework\Core\ValueObjects\Duration;
use App\Framework\HttpClient\Exception\ServerErrorException;
use App\Framework\Validation\Exceptions\ValidationException;

/**
 * Usage examples for the new FailurePredicate system
 */
final class FailurePredicateUsageExample
{
    /**
     * Example 1: Using factory methods for common scenarios
     */
    public function exampleFactoryUsage(): void
    {
        // For external services - triggers on server errors, excludes validation errors
        $externalServiceConfig = CircuitBreakerConfig::forExternalService();

        // For database operations - only triggers on database-related exceptions
        $databaseConfig = CircuitBreakerConfig::forDatabase();

        // For HTTP clients - comprehensive HTTP error handling
        $httpClientConfig = CircuitBreakerConfig::forHttpClient();

        // Only timeout errors
        $timeoutConfig = CircuitBreakerConfig::forTimeoutErrors();

        // Critical errors only (500+ status codes, framework errors)
        $criticalConfig = CircuitBreakerConfig::strict();
    }

    /**
     * Example 2: Using the fluent builder for custom predicates
     */
    public function exampleBuilderUsage(): void
    {
        // Complex predicate: Include server errors OR specific exceptions, exclude validation
        $customPredicate = FailurePredicateFactory::builder()
            ->includeServerErrors()  // 5xx HTTP status codes
            ->includeExceptions([
                ServerErrorException::class,
                \PDOException::class,
            ])
            ->excludeExceptions([
                ValidationException::class,
                \InvalidArgumentException::class,
            ])
            ->includeStatusCodes([429, 503, 504])  // Rate limiting and gateway errors
            ->addCallback(
                fn (\Throwable $e, string $service) => str_contains($e->getMessage(), 'timeout'),
                'Timeout detection'
            )
            ->or()  // Use OR logic instead of AND
            ->build();

        $config = CircuitBreakerConfig::withFailurePredicate(
            failurePredicate: $customPredicate,
            failureThreshold: 3,
            recoveryTimeout: Duration::fromMinutes(2)
        );
    }

    /**
     * Example 3: Service-specific predicates
     */
    public function exampleServiceSpecificPredicates(): void
    {
        // Different predicates for different services
        $paymentServicePredicate = FailurePredicateFactory::builder()
            ->includeServerErrors()
            ->includeStatusCodes([429, 502, 503, 504])
            ->excludeExceptions([ValidationException::class])
            ->and()
            ->build();

        $emailServicePredicate = FailurePredicateFactory::builder()
            ->includeExceptions([
                \App\Framework\HttpClient\Exception\CurlExecutionFailed::class,
                ServerErrorException::class,
            ])
            ->addCallback(
                fn (\Throwable $e, string $service) => $service === 'email-service' &&
                    str_contains($e->getMessage(), 'rate limit'),
                'Email service rate limiting'
            )
            ->or()
            ->build();

        $paymentConfig = CircuitBreakerConfig::withFailurePredicate($paymentServicePredicate);
        $emailConfig = CircuitBreakerConfig::withFailurePredicate($emailServicePredicate);
    }

    /**
     * Example 4: Advanced combinations with logical operators
     */
    public function exampleAdvancedCombinations(): void
    {
        // Predicate that triggers when:
        // (Server errors OR specific exceptions) AND NOT validation errors
        $complexPredicate = FailurePredicateFactory::builder()
            ->includeServerErrors()
            ->includeExceptions([
                ServerErrorException::class,
                \RuntimeException::class,
            ])
            ->and()  // AND logic for above conditions
            ->build();

        // Combine with NOT predicate for validation errors
        $validationExclusion = FailurePredicateFactory::builder()
            ->includeExceptions([
                ValidationException::class,
                \InvalidArgumentException::class,
            ])
            ->build();

        // This creates: (ServerErrors OR RuntimeException) AND NOT ValidationErrors
        $finalPredicate = FailurePredicateFactory::builder()
            ->addPredicate($complexPredicate)
            ->addPredicate(\App\Framework\CircuitBreaker\FailurePredicate\CompositeFailurePredicate::not([$validationExclusion]))
            ->and()
            ->build();

        $config = CircuitBreakerConfig::withFailurePredicate($finalPredicate);
    }

    /**
     * Example 5: Message-based predicates
     */
    public function exampleMessageBasedPredicates(): void
    {
        // Trigger only on timeout-related messages
        $timeoutPredicate = FailurePredicateFactory::builder()
            ->addCallback(
                \App\Framework\CircuitBreaker\FailurePredicate\CallbackFailurePredicate::messageContains('timeout')
                    ->shouldTrigger(...),
                'Timeout message detection'
            )
            ->addCallback(
                \App\Framework\CircuitBreaker\FailurePredicate\CallbackFailurePredicate::messageMatches('/connection.*failed/i')
                    ->shouldTrigger(...),
                'Connection failure pattern'
            )
            ->or()
            ->build();

        $config = CircuitBreakerConfig::withFailurePredicate($timeoutPredicate);
    }

    /**
     * Example 6: Testing and debugging predicates
     */
    public function exampleTestingPredicates(): void
    {
        // Never trigger (useful for testing)
        $neverTrigger = FailurePredicateFactory::never();

        // Always trigger (useful for testing)
        $alwaysTrigger = FailurePredicateFactory::always();

        // Custom test predicate
        $testPredicate = FailurePredicateFactory::builder()
            ->addCallback(
                fn (\Throwable $e, string $service) =>
                    $service === 'test-service' && get_class($e) === \RuntimeException::class,
                'Test-specific predicate'
            )
            ->build();

        $testConfig = CircuitBreakerConfig::withFailurePredicate($testPredicate);
    }
}
