<?php

declare(strict_types=1);

namespace App\Framework\Quality\PHPStan\Rules;

use PhpParser\Node;
use PhpParser\Node\Stmt\Return_;
use PHPStan\Analyser\Scope;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;

/**
 * @implements Rule<Return_>
 */
final class PreferValueObjectsRule implements Rule
{
    /** @var array<string> */
    private array $suspiciousReturnPatterns = [
        'metrics',
        'stats',
        'report',
        'summary',
        'analysis',
        'data',
    ];

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

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

        if ($node->expr === null) {
            return [];
        }

        // Check if we're in a method that should return Value Objects
        $function = $scope->getFunction();
        if ($function === null) {
            return [];
        }

        $methodName = $function->getName();
        $className = $scope->getClassReflection()?->getName() ?? '';

        // Skip if not in Application layer (allow primitive returns in Framework)
        if (! str_starts_with($className, 'App\\Application\\')) {
            return [];
        }

        // Check if method name suggests it should return a Value Object
        $shouldUseValueObject = false;
        foreach ($this->suspiciousReturnPatterns as $pattern) {
            if (str_contains(strtolower($methodName), $pattern)) {
                $shouldUseValueObject = true;

                break;
            }
        }

        if (! $shouldUseValueObject) {
            return [];
        }

        return [
            RuleErrorBuilder::message(sprintf(
                'Method %s::%s() returns an array but should consider using a Value Object for better type safety and domain modeling',
                $scope->getClassReflection()?->getDisplayName() ?? 'Unknown',
                $methodName
            ))->tip('Create a dedicated Value Object class instead of returning raw arrays')->build(),
        ];
    }
}
