<?php

declare(strict_types=1);

namespace App\Framework\Worker;

use App\Framework\Core\ValueObjects\Duration;
use App\Framework\Discovery\Results\DiscoveryRegistry;
use App\Framework\Scheduler\Schedules\IntervalSchedule;
use App\Framework\Scheduler\Services\SchedulerService;
use ReflectionClass;

/**
 * Discovers and registers classes with #[Schedule] attribute
 */
final readonly class ScheduleDiscoveryService
{
    public function __construct(
        private DiscoveryRegistry $discoveryRegistry,
        private SchedulerService $schedulerService
    ) {
    }

    /**
     * Discover and register all scheduled jobs
     */
    public function discoverAndRegister(): int
    {
        $registered = 0;

        // Find all classes with Schedule attribute via DiscoveryRegistry
        $discoveredAttributes = $this->discoveryRegistry->attributes()->get(Schedule::class);

        foreach ($discoveredAttributes as $discoveredAttribute) {
            $className = $discoveredAttribute->className->getFullyQualified();
            $reflection = new ReflectionClass($className);
            $attributes = $reflection->getAttributes(Schedule::class);

            foreach ($attributes as $attribute) {
                /** @var Schedule $schedule */
                $schedule = $attribute->newInstance();

                // Create task ID from class name
                $taskId = $this->generateTaskId($className);

                // Create schedule from Every configuration
                $intervalSeconds = $schedule->at->toSeconds();
                $intervalSchedule = IntervalSchedule::every(
                    Duration::fromSeconds($intervalSeconds)
                );

                // Register task with scheduler
                $this->schedulerService->schedule(
                    taskId: $taskId,
                    schedule: $intervalSchedule,
                    task: function () use ($className) {
                        // Instantiate and execute the scheduled job
                        $job = new $className();

                        if (method_exists($job, 'handle')) {
                            return $job->handle();
                        }

                        if (is_callable($job)) {
                            return $job();
                        }

                        throw new \RuntimeException(
                            "Scheduled job {$className} must have handle() method or be callable"
                        );
                    }
                );

                $registered++;
            }
        }

        return $registered;
    }

    /**
     * Generate a task ID from class name
     */
    private function generateTaskId(string $className): string
    {
        // Extract short class name
        $parts = explode('\\', $className);
        $shortName = end($parts);

        // Convert from PascalCase to kebab-case
        $taskId = strtolower(preg_replace('/([a-z])([A-Z])/', '$1-$2', $shortName));

        return $taskId;
    }

    /**
     * Get all registered scheduled tasks
     */
    public function getScheduledTasks(): array
    {
        return $this->schedulerService->getScheduledTasks();
    }
}
