<?php

declare(strict_types=1);

namespace App\Framework\Monitoring;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Core\ValueObjects\Percentage;
use App\Framework\Core\ValueObjects\Timestamp;

/**
 * Metrics value object for circuit breaker monitoring
 */
final readonly class CircuitBreakerMetrics
{
    public function __construct(
        public string $circuitName,
        public int $totalRequests,
        public int $totalFailures,
        public int $totalSuccesses,
        public Duration $averageResponseTime,
        public Duration $peakResponseTime,
        public string $currentState, // OPEN, CLOSED, HALF_OPEN
        public Timestamp $lastStateChange,
        public int $consecutiveFailures,
        public ?Duration $timeUntilRetry = null,
    ) {
    }

    /**
     * Get failure rate as percentage
     */
    public function getFailureRate(): Percentage
    {
        if ($this->totalRequests === 0) {
            return Percentage::fromFloat(0.0);
        }

        return Percentage::fromFloat($this->totalFailures / $this->totalRequests);
    }

    /**
     * Get success rate as percentage
     */
    public function getSuccessRate(): Percentage
    {
        if ($this->totalRequests === 0) {
            return Percentage::fromFloat(0.0);
        }

        return Percentage::fromFloat($this->totalSuccesses / $this->totalRequests);
    }

    /**
     * Check if circuit is healthy
     */
    public function isHealthy(): bool
    {
        return $this->currentState === 'CLOSED' &&
               $this->getFailureRate()->toFloat() < 0.05; // Less than 5% failure rate
    }

    /**
     * Check if circuit is degraded
     */
    public function isDegraded(): bool
    {
        return $this->currentState === 'HALF_OPEN' ||
               ($this->currentState === 'CLOSED' && $this->getFailureRate()->toFloat() >= 0.05);
    }

    /**
     * Check if circuit is failed
     */
    public function isFailed(): bool
    {
        return $this->currentState === 'OPEN';
    }

    /**
     * Get health status string
     */
    public function getHealthStatus(): string
    {
        if ($this->isFailed()) {
            return 'failed';
        }

        if ($this->isDegraded()) {
            return 'degraded';
        }

        return 'healthy';
    }

    /**
     * Get time since last state change
     */
    public function getTimeSinceLastStateChange(Timestamp $currentTime): Duration
    {
        return $this->lastStateChange->diff($currentTime);
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            'circuit_name' => $this->circuitName,
            'total_requests' => $this->totalRequests,
            'total_failures' => $this->totalFailures,
            'total_successes' => $this->totalSuccesses,
            'failure_rate' => $this->getFailureRate()->toFloat(),
            'success_rate' => $this->getSuccessRate()->toFloat(),
            'average_response_time_ms' => $this->averageResponseTime->toMilliseconds(),
            'peak_response_time_ms' => $this->peakResponseTime->toMilliseconds(),
            'current_state' => $this->currentState,
            'last_state_change' => $this->lastStateChange->toIsoString(),
            'consecutive_failures' => $this->consecutiveFailures,
            'time_until_retry_ms' => $this->timeUntilRetry?->toMilliseconds(),
            'health_status' => $this->getHealthStatus(),
            'is_healthy' => $this->isHealthy(),
            'is_degraded' => $this->isDegraded(),
            'is_failed' => $this->isFailed(),
        ];
    }

    /**
     * Create from array
     */
    public static function fromArray(array $data): self
    {
        return new self(
            circuitName: $data['circuit_name'],
            totalRequests: $data['total_requests'],
            totalFailures: $data['total_failures'],
            totalSuccesses: $data['total_successes'],
            averageResponseTime: Duration::fromMilliseconds($data['average_response_time_ms']),
            peakResponseTime: Duration::fromMilliseconds($data['peak_response_time_ms']),
            currentState: $data['current_state'],
            lastStateChange: Timestamp::fromFloat(strtotime($data['last_state_change'])),
            consecutiveFailures: $data['consecutive_failures'],
            timeUntilRetry: isset($data['time_until_retry_ms'])
                ? Duration::fromMilliseconds($data['time_until_retry_ms'])
                : null,
        );
    }
}
