<?php

declare(strict_types=1);

namespace App\Framework\ErrorBoundaries\CircuitBreaker;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\StateManagement\SerializableState;

/**
 * Value object representing circuit breaker state for error boundaries
 */
final readonly class BoundaryCircuitBreakerState implements SerializableState
{
    public function __construct(
        public int $failureCount = 0,
        public int $successCount = 0,
        public ?Timestamp $lastFailureTime = null,
        public ?Timestamp $openedAt = null,
        public BoundaryCircuitState $state = BoundaryCircuitState::CLOSED,
        public int $halfOpenAttempts = 0,
    ) {
    }

    /**
     * Check if circuit is open
     */
    public function isOpen(): bool
    {
        return $this->state === BoundaryCircuitState::OPEN;
    }

    /**
     * Check if circuit is closed
     */
    public function isClosed(): bool
    {
        return $this->state === BoundaryCircuitState::CLOSED;
    }

    /**
     * Check if circuit is half-open
     */
    public function isHalfOpen(): bool
    {
        return $this->state === BoundaryCircuitState::HALF_OPEN;
    }

    /**
     * Record a failure and return new state
     */
    public function recordFailure(): self
    {
        return new self(
            failureCount: $this->failureCount + 1,
            successCount: $this->successCount,
            lastFailureTime: Timestamp::now(),
            openedAt: $this->openedAt,
            state: $this->state,
            halfOpenAttempts: $this->halfOpenAttempts,
        );
    }

    /**
     * Record a success and return new state
     */
    public function recordSuccess(): self
    {
        return new self(
            failureCount: 0, // Reset failure count on success
            successCount: $this->successCount + 1,
            lastFailureTime: $this->lastFailureTime,
            openedAt: null, // Reset opened time
            state: $this->state,
            halfOpenAttempts: 0, // Reset half-open attempts
        );
    }

    /**
     * Transition to OPEN state
     */
    public function transitionToOpen(): self
    {
        return new self(
            failureCount: $this->failureCount,
            successCount: $this->successCount,
            lastFailureTime: $this->lastFailureTime,
            openedAt: Timestamp::now(),
            state: BoundaryCircuitState::OPEN,
            halfOpenAttempts: 0,
        );
    }

    /**
     * Transition to HALF_OPEN state
     */
    public function transitionToHalfOpen(): self
    {
        return new self(
            failureCount: $this->failureCount,
            successCount: $this->successCount,
            lastFailureTime: $this->lastFailureTime,
            openedAt: $this->openedAt,
            state: BoundaryCircuitState::HALF_OPEN,
            halfOpenAttempts: 0,
        );
    }

    /**
     * Transition to CLOSED state
     */
    public function transitionToClosed(): self
    {
        return new self(
            failureCount: 0, // Reset failure count
            successCount: $this->successCount,
            lastFailureTime: null, // Reset last failure time
            openedAt: null, // Reset opened time
            state: BoundaryCircuitState::CLOSED,
            halfOpenAttempts: 0,
        );
    }

    /**
     * Increment half-open attempts
     */
    public function incrementHalfOpenAttempts(): self
    {
        return new self(
            failureCount: $this->failureCount,
            successCount: $this->successCount,
            lastFailureTime: $this->lastFailureTime,
            openedAt: $this->openedAt,
            state: $this->state,
            halfOpenAttempts: $this->halfOpenAttempts + 1,
        );
    }

    /**
     * Check if failure threshold is exceeded
     */
    public function exceedsFailureThreshold(int $threshold): bool
    {
        return $this->failureCount >= $threshold;
    }

    /**
     * Check if success threshold is met for half-open state
     */
    public function meetsSuccessThreshold(int $threshold): bool
    {
        return $this->successCount >= $threshold;
    }

    /**
     * Check if half-open attempts exceed maximum
     */
    public function exceedsHalfOpenAttempts(int $maxAttempts): bool
    {
        return $this->halfOpenAttempts >= $maxAttempts;
    }

    /**
     * Convert to array for serialization
     */
    public function toArray(): array
    {
        return [
            'failure_count' => $this->failureCount,
            'success_count' => $this->successCount,
            'last_failure_time' => $this->lastFailureTime?->toFloat(),
            'opened_at' => $this->openedAt?->toFloat(),
            'state' => $this->state->value,
            'half_open_attempts' => $this->halfOpenAttempts,
        ];
    }

    /**
     * Create from array (deserialization)
     */
    public static function fromArray(array $data): static
    {
        return new self(
            failureCount: $data['failure_count'] ?? 0,
            successCount: $data['success_count'] ?? 0,
            lastFailureTime: isset($data['last_failure_time'])
                ? Timestamp::fromFloat($data['last_failure_time'])
                : null,
            openedAt: isset($data['opened_at'])
                ? Timestamp::fromFloat($data['opened_at'])
                : null,
            state: BoundaryCircuitState::from($data['state'] ?? 'closed'),
            halfOpenAttempts: $data['half_open_attempts'] ?? 0,
        );
    }
}
