<?php

declare(strict_types=1);

namespace App\Framework\Quality\PHPStan\Rules;

use PhpParser\Node;
use PhpParser\Node\Expr\FuncCall;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

/**
 * @implements Rule<FuncCall>
 */
final class UseTimerAbstractionRule implements Rule
{
    /** @var array<string, string> */
    private array $forbiddenSleepFunctions = [
        'sleep' => 'Use Timer::sleep(Duration) instead for testable sleep operations',
        'usleep' => 'Use Timer::sleep(Duration) instead for testable sleep operations',
        'time_sleep_until' => 'Use Timer::sleep(Duration) with Clock abstraction instead',
        'time_nanosleep' => 'Use Timer::sleep(Duration) instead',
    ];

    public function getNodeType(): string
    {
        return FuncCall::class;
    }

    public function processNode(Node $node, Scope $scope): array
    {
        if (! $node instanceof FuncCall) {
            return [];
        }

        if (! $node->name instanceof Node\Name) {
            return [];
        }

        $functionName = $node->name->toString();

        // Skip if we're already in Framework DateTime layer (allow direct usage there)
        $currentClass = $scope->getClassReflection()?->getName() ?? '';
        if (str_starts_with($currentClass, 'App\\Framework\\DateTime\\')) {
            return [];
        }

        if (! isset($this->forbiddenSleepFunctions[$functionName])) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Function %s() is forbidden: %s',
                $functionName,
                $this->forbiddenSleepFunctions[$functionName]
            ))
            ->tip('Inject Timer interface via dependency injection for testable timing operations. Use Duration value objects for type-safe timing.')
            ->build(),
        ];
    }
}
