<?php

declare(strict_types=1);

namespace App\Framework\LiveComponents\Polling;

use App\Framework\Core\ValueObjects\Duration;
use Closure;

/**
 * Value Object wrapping a closure with polling configuration.
 *
 * Allows inline poll definitions in templates without requiring separate methods.
 *
 * @example
 * ```php
 * return new ViewResult('dashboard', [
 *     'notifications' => new PollableClosure(
 *         closure: fn() => $this->notificationService->getUnread(),
 *         interval: 1000
 *     )
 * ]);
 * ```
 */
final readonly class PollableClosure
{
    /**
     * @param Closure $closure The closure to execute
     * @param int $interval Polling interval in milliseconds (minimum: 100ms)
     * @param bool $enabled Whether polling is enabled
     * @param string|null $event Optional event name to dispatch on poll
     * @param bool $stopOnError Whether to stop polling if closure throws
     */
    public function __construct(
        public Closure $closure,
        public int $interval = 1000,
        public bool $enabled = true,
        public ?string $event = null,
        public bool $stopOnError = false
    ) {
        if ($interval < 100) {
            throw new \InvalidArgumentException('Poll interval must be at least 100ms');
        }

        if ($interval > 300000) {
            throw new \InvalidArgumentException('Poll interval cannot exceed 5 minutes (300000ms)');
        }
    }

    /**
     * Execute the closure.
     */
    public function __invoke(): mixed
    {
        return ($this->closure)();
    }

    /**
     * Execute the closure (explicit method).
     */
    public function execute(): mixed
    {
        return ($this->closure)();
    }

    /**
     * Get interval as Duration value object.
     */
    public function getInterval(): Duration
    {
        return Duration::fromMilliseconds($this->interval);
    }

    /**
     * Create new instance with different enabled state.
     */
    public function withEnabled(bool $enabled): self
    {
        return new self(
            closure: $this->closure,
            interval: $this->interval,
            enabled: $enabled,
            event: $this->event,
            stopOnError: $this->stopOnError
        );
    }

    /**
     * Create new instance with different interval.
     */
    public function withInterval(int $interval): self
    {
        return new self(
            closure: $this->closure,
            interval: $interval,
            enabled: $this->enabled,
            event: $this->event,
            stopOnError: $this->stopOnError
        );
    }

    /**
     * Create simple pollable closure with just interval.
     */
    public static function simple(Closure $closure, int $interval = 1000): self
    {
        return new self(closure: $closure, interval: $interval);
    }

    /**
     * Check if closure should be polled.
     */
    public function shouldPoll(): bool
    {
        return $this->enabled;
    }

    /**
     * Get polling metadata for JavaScript generation.
     */
    public function getMetadata(): array
    {
        return [
            'interval' => $this->interval,
            'enabled' => $this->enabled,
            'event' => $this->event,
            'stopOnError' => $this->stopOnError
        ];
    }
}
