<?php
declare(strict_types=1);

namespace PpPhp\Core\Platform\Event\Distributed\Scheduler;

use PpPhp\Core\Platform\Event\Distributed\DistributedRuntimeCoordinator;

final class DistributedWorkerSelector
{
    public function __construct(
        private readonly DistributedRuntimeCoordinator $runtime,
        private readonly DistributedSchedulePolicy $policy = new DistributedSchedulePolicy()
    ) {}

    public function select(?DistributedScheduleJob $job = null): ?array
    {
        $workers = $this->runtime->registry()->toArray();

        $workers = array_values(array_filter($workers, static function (array $worker): bool {
            return (int) ($worker['availableCapacity'] ?? 0) > 0
                && (string) ($worker['status'] ?? '') !== 'offline';
        }));

        if ($job !== null && $job->affinity() !== []) {
            $affinity = $job->affinity();
            $workers = array_values(array_filter($workers, static function (array $worker) use ($affinity): bool {
                if (($affinity['nodeId'] ?? '') !== '' && ($worker['nodeId'] ?? '') !== $affinity['nodeId']) {
                    return false;
                }

                if (($affinity['workerId'] ?? '') !== '' && ($worker['workerId'] ?? '') !== $affinity['workerId']) {
                    return false;
                }

                return true;
            }));
        }

        if ($workers === []) {
            return null;
        }

        $strategy = $this->policy->strategy();

        usort($workers, static function (array $a, array $b) use ($strategy): int {
            if ($strategy === DistributedSchedulePolicy::HIGHEST_CAPACITY) {
                return ((int) ($b['capacity'] ?? 0)) <=> ((int) ($a['capacity'] ?? 0));
            }

            if ($strategy === DistributedSchedulePolicy::ROUND_ROBIN) {
                return strcmp((string) ($a['workerId'] ?? ''), (string) ($b['workerId'] ?? ''));
            }

            $aLoad = (int) ($a['activeJobs'] ?? 0);
            $bLoad = (int) ($b['activeJobs'] ?? 0);
            if ($aLoad === $bLoad) {
                return ((int) ($b['availableCapacity'] ?? 0)) <=> ((int) ($a['availableCapacity'] ?? 0));
            }

            return $aLoad <=> $bLoad;
        });

        return $workers[0];
    }
}
