<?php

declare(strict_types=1);

namespace App\Framework\CircuitBreaker;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Exception\DatabaseException;
use PDOException;
use Throwable;

/**
 * Circuit Breaker speziell für Datenbankverbindungen
 */
final readonly class DatabaseCircuitBreaker
{
    private const string SERVICE_NAME = 'database';

    public function __construct(
        private CircuitBreaker $circuitBreaker,
        private ?CircuitBreakerConfig $config = null
    ) {
    }

    /**
     * Führt eine Datenbankoperation mit Circuit Breaker Schutz aus
     *
     * @template T
     * @param callable(ConnectionInterface): T $operation
     * @return T
     * @throws CircuitBreakerException|DatabaseException|Throwable
     */
    public function execute(ConnectionInterface $connection, callable $operation): mixed
    {
        $config = $this->config ?? $this->getDefaultDatabaseConfig();

        return $this->circuitBreaker->execute(
            service: self::SERVICE_NAME,
            operation: function () use ($connection, $operation) {
                try {
                    return $operation($connection);
                } catch (PDOException $e) {
                    // PDO Exceptions in DatabaseException wrappen für einheitliche Behandlung
                    throw new DatabaseException(
                        "Database operation failed: " . $e->getMessage(),
                        previous: $e
                    );
                }
            },
            config: $config
        );
    }

    /**
     * Prüft die Datenbankverbindung
     */
    public function healthCheck(ConnectionInterface $connection): bool
    {
        try {
            $this->execute($connection, function ($conn) {
                // Einfache Query für Health Check
                $conn->query('SELECT 1');

                return true;
            });

            return true;
        } catch (Throwable) {
            return false;
        }
    }

    /**
     * Gibt den aktuellen Status des Database Circuit Breakers zurück
     */
    public function getStatus(): array
    {
        $metrics = $this->circuitBreaker->getMetrics(self::SERVICE_NAME);

        return [
            'service' => self::SERVICE_NAME,
            'state' => $metrics['state'],
            'failure_count' => $metrics['failure_count'],
            'success_count' => $metrics['success_count'],
            'last_failure_time' => $metrics['last_failure_time'],
            'health_status' => $metrics['state'] === 'closed' ? 'healthy' : 'degraded',
        ];
    }

    /**
     * Setzt den Database Circuit Breaker zurück
     */
    public function reset(): void
    {
        $this->circuitBreaker->reset(self::SERVICE_NAME);
    }

    /**
     * Standard-Konfiguration für Datenbank Circuit Breaker
     */
    private function getDefaultDatabaseConfig(): CircuitBreakerConfig
    {
        return new CircuitBreakerConfig(
            failureThreshold: 3,
            recoveryTimeout: Duration::fromSeconds(30),
            halfOpenMaxAttempts: 2,
            successThreshold: 2
        );
    }
}
