<?php

declare(strict_types=1);

namespace App\Framework\Database\HealthCheck;

use App\Framework\Database\ConnectionInterface;
use App\Framework\Database\Exception\DatabaseException;

final readonly class ConnectionHealthChecker
{
    public function __construct(
        private int $timeoutSeconds = 5,
        private array $customQueries = []
    ) {}

    /**
     * Führt einen einfachen Health Check aus
     */
    public function checkHealth(ConnectionInterface $connection): HealthCheckResult
    {
        $startTime = microtime(true);

        try {
            // Einfacher SELECT zum Testen der Verbindung
            $result = $connection->queryScalar('SELECT 1');

            $responseTime = (microtime(true) - $startTime) * 1000; // in Millisekunden

            if ($result != 1) {
                return HealthCheckResult::unhealthy(
                    $responseTime,
                    'Unexpected result from health check query',
                    null,
                    ['expected' => 1, 'actual' => $result]
                );
            }

            return HealthCheckResult::healthy($responseTime, 'Connection is healthy');

        } catch (\Throwable $e) {
            $responseTime = (microtime(true) - $startTime) * 1000;
            return HealthCheckResult::error($e, $responseTime);
        }
    }

    /**
     * Führt einen Health Check mit Timeout aus
     */
    public function checkHealthWithTimeout(ConnectionInterface $connection): HealthCheckResult
    {
        $startTime = time();

        while ((time() - $startTime) < $this->timeoutSeconds) {
            $result = $this->checkHealth($connection);

            if ($result->isHealthy) {
                return $result;
            }

            // Kurz warten vor nächstem Versuch
            usleep(100000); // 100ms
        }

        return HealthCheckResult::timeout($this->timeoutSeconds * 1000);
    }

    /**
     * Führt erweiterte Health Checks aus
     */
    public function checkDetailedHealth(ConnectionInterface $connection): HealthCheckResult
    {
        $startTime = microtime(true);
        $additionalData = [];

        try {
            // 1. Basis Health Check
            $basicResult = $this->checkHealth($connection);
            if (!$basicResult->isHealthy) {
                return $basicResult;
            }

            $additionalData['basic_check'] = $basicResult->toArray();

            // 2. PDO-Status prüfen
            $pdoStatus = $this->checkPdoStatus($connection);
            $additionalData['pdo_status'] = $pdoStatus;

            // 3. Custom Queries ausführen
            if (!empty($this->customQueries)) {
                $customResults = $this->executeCustomQueries($connection);
                $additionalData['custom_queries'] = $customResults;
            }

            // 4. Connection Attributes prüfen
            $attributes = $this->getConnectionAttributes($connection);
            $additionalData['connection_attributes'] = $attributes;

            $responseTime = (microtime(true) - $startTime) * 1000;

            return HealthCheckResult::healthy(
                $responseTime,
                'Detailed health check passed',
                $additionalData
            );

        } catch (\Throwable $e) {
            $responseTime = (microtime(true) - $startTime) * 1000;
            return HealthCheckResult::error($e, $responseTime)->withAdditionalData('partial_data', $additionalData);
        }
    }

    /**
     * Prüft ob die Connection noch lebendig ist (schneller Check)
     */
    public function checkConnectionAlive(ConnectionInterface $connection): bool
    {
        try {
            $pdo = $connection->getPdo();

            // Prüfe ob die PDO-Verbindung noch aktiv ist
            $stmt = $pdo->query('SELECT 1');
            return $stmt !== false;

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

    /**
     * Prüft den PDO-Status
     */
    private function checkPdoStatus(ConnectionInterface $connection): array
    {
        try {
            $pdo = $connection->getPdo();

            return [
                'connection_status' => $pdo->getAttribute(\PDO::ATTR_CONNECTION_STATUS),
                'server_info' => $pdo->getAttribute(\PDO::ATTR_SERVER_INFO),
                'driver_name' => $pdo->getAttribute(\PDO::ATTR_DRIVER_NAME),
                'client_version' => $pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION),
                'server_version' => $pdo->getAttribute(\PDO::ATTR_SERVER_VERSION),
                'in_transaction' => $connection->inTransaction(),
            ];
        } catch (\Throwable $e) {
            return ['error' => $e->getMessage()];
        }
    }

    /**
     * Führt benutzerdefinierte Queries aus
     */
    private function executeCustomQueries(ConnectionInterface $connection): array
    {
        $results = [];

        foreach ($this->customQueries as $name => $query) {
            try {
                $startTime = microtime(true);
                $result = $connection->queryScalar($query);
                $responseTime = (microtime(true) - $startTime) * 1000;

                $results[$name] = [
                    'success' => true,
                    'result' => $result,
                    'response_time_ms' => round($responseTime, 2),
                ];
            } catch (\Throwable $e) {
                $results[$name] = [
                    'success' => false,
                    'error' => $e->getMessage(),
                    'response_time_ms' => 0,
                ];
            }
        }

        return $results;
    }

    /**
     * Sammelt Connection-Attribute
     */
    private function getConnectionAttributes(ConnectionInterface $connection): array
    {
        try {
            $pdo = $connection->getPdo();

            $attributes = [];
            $attributesToCheck = [
                'AUTOCOMMIT' => \PDO::ATTR_AUTOCOMMIT,
                'ERRMODE' => \PDO::ATTR_ERRMODE,
                'CASE' => \PDO::ATTR_CASE,
                'NULL_TO_STRING' => \PDO::ATTR_NULL_TO_STRING,
                'STRINGIFY_FETCHES' => \PDO::ATTR_STRINGIFY_FETCHES,
                'STATEMENT_CLASS' => \PDO::ATTR_STATEMENT_CLASS,
                'TIMEOUT' => \PDO::ATTR_TIMEOUT,
                'EMULATE_PREPARES' => \PDO::ATTR_EMULATE_PREPARES,
                'DEFAULT_FETCH_MODE' => \PDO::ATTR_DEFAULT_FETCH_MODE,
            ];

            foreach ($attributesToCheck as $name => $constant) {
                try {
                    $attributes[$name] = $pdo->getAttribute($constant);
                } catch (\Throwable) {
                    $attributes[$name] = 'Not supported';
                }
            }

            return $attributes;
        } catch (\Throwable $e) {
            return ['error' => $e->getMessage()];
        }
    }

    /**
     * Factory-Methoden für verschiedene Health Check-Konfigurationen
     */
    public static function quick(int $timeoutSeconds = 2): self
    {
        return new self($timeoutSeconds);
    }

    public static function detailed(int $timeoutSeconds = 10, array $customQueries = []): self
    {
        return new self($timeoutSeconds, $customQueries);
    }

    public static function withCustomQueries(array $customQueries, int $timeoutSeconds = 5): self
    {
        return new self($timeoutSeconds, $customQueries);
    }
}
