<?php

declare(strict_types=1);

namespace App\Framework\Quality\PHPStan\Rules;

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

/**
 * @implements Rule<New_>
 */
final class UseDateTimeAbstractionRule implements Rule
{
    /** @var array<string, string> */
    private array $forbiddenDateTimeClasses = [
        'DateTime' => 'Use App\\Framework\\DateTime\\DateTime instead for consistent timezone handling',
        'DateTimeImmutable' => 'Use Clock::now() or Clock::fromString() instead for testable time handling',
        'DateInterval' => 'Use App\\Framework\\DateTime\\DateTime::createInterval() instead for consistent error handling',
        'DatePeriod' => 'Use App\\Framework\\DateTime\\DateRange instead for better domain modeling',
    ];

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

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

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

        $className = $node->class->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->forbiddenDateTimeClasses[$className])) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Direct instantiation of %s is forbidden: %s',
                $className,
                $this->forbiddenDateTimeClasses[$className]
            ))
            ->tip('Use Clock interface or Framework DateTime abstractions via dependency injection')
            ->build(),
        ];
    }
}
