<?php

declare(strict_types=1);

namespace App\Framework\Async;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\DateTime\Clock;
use App\Framework\DateTime\Timer;
use Fiber;

/**
 * Service für asynchrone Operationen mit Composition-Pattern
 */
final readonly class AsyncService
{
    public function __construct(
        private FiberManager $fiberManager,
        private AsyncPromiseFactory $promiseFactory,
        private AsyncTimer $asyncTimer,
        private Clock $clock,
        private Timer $timer
    ) {
    }

    /**
     * Run operation asynchronously
     */
    public function async(callable $operation): Fiber
    {
        return $this->fiberManager->async($operation);
    }

    /**
     * Run operation asynchronously with promise
     */
    public function promise(callable $operation): AsyncPromise
    {
        return $this->promiseFactory->create($operation);
    }

    /**
     * Run multiple operations in parallel (variadic)
     */
    public function parallel(callable ...$operations): AsyncPromise
    {
        $promises = [];
        foreach ($operations as $operation) {
            $promises[] = $this->promiseFactory->create($operation);
        }

        return $this->promiseFactory->all(...$promises);
    }

    /**
     * Run operation with timeout
     */
    public function withTimeout(callable $operation, Duration $timeout): mixed
    {
        return $this->fiberManager->withTimeoutDuration($operation, $timeout);
    }

    /**
     * Delay execution
     */
    public function delay(Duration $duration): Fiber
    {
        return $this->asyncTimer->sleepDuration($duration);
    }

    /**
     * Measure execution time
     */
    public function measure(callable $operation): AsyncPromise
    {
        $start = $this->clock->time();

        return $this->promise($operation)->then(function ($result) use ($start) {
            $duration = $start->age($this->clock);

            return [
                'result' => $result,
                'duration' => $duration,
                'milliseconds' => $duration->toMilliseconds(),
            ];
        });
    }

    /**
     * Wait for condition
     */
    public function waitFor(
        callable $condition,
        ?Duration $timeout = null,
        ?Duration $checkInterval = null
    ): Fiber {
        return $this->asyncTimer->waitForDuration($condition, $timeout, $checkInterval);
    }

    /**
     * Schedule callback after delay
     */
    public function schedule(callable $callback, Duration $delay): string
    {
        return $this->asyncTimer->setTimeoutDuration($callback, $delay);
    }

    /**
     * Schedule recurring callback
     */
    public function repeat(callable $callback, Duration $interval): string
    {
        return $this->asyncTimer->setIntervalDuration($callback, $interval);
    }

    /**
     * Cancel scheduled operation
     */
    public function cancel(string $id): bool
    {
        return $this->asyncTimer->clear($id);
    }

    /**
     * Batch operations with concurrency control (variadic)
     */
    public function batch(int $maxConcurrency = 10, callable ...$operations): array
    {
        return $this->fiberManager->throttled($operations, $maxConcurrency);
    }

    /**
     * Get async statistics
     */
    public function getStats(): array
    {
        return [
            'fiber_manager' => $this->fiberManager->getStats(),
            'async_timer' => $this->asyncTimer->getStats(),
        ];
    }
}
