<?php

declare(strict_types=1);

namespace App\Framework\Validation;

use App\Framework\Core\ValueObjects\ClassName;
use App\Framework\Reflection\ReflectionProvider;

final readonly class Validator
{
    public function __construct(
        private ReflectionProvider $reflectionProvider
    ) {
    }

    /**
     * Validiert ein Objekt und gibt das Ergebnis zurück
     *
     * @param object $object Das zu validierende Objekt
     * @param string|null $group Optionale Validierungsgruppe
     * @return ValidationResult
     */
    public function validate(object $object, ?string $group = null): ValidationResult
    {
        $result = new ValidationResult();
        $className = ClassName::create($object::class);
        $properties = $this->reflectionProvider->getProperties($className);

        // Eigenschaften validieren
        foreach ($properties as $property) {
            $propertyName = $property->getName();
            $allowsNull = $property->getType()?->allowsNull();

            try {
                // Versuche zuerst den Wert zu lesen
                $value = $property->getValue($object);

                // Prüfe nur den tatsächlichen Wert, nicht isInitialized()
                if ($value === null && ! $allowsNull) {
                    $result->addError(
                        $property->getName(),
                        sprintf("Feld '%s' darf nicht null sein.", $property->getName())
                    );

                    continue;
                }

            } catch (\Throwable $e) {
                // Wenn wir den Wert nicht lesen können, prüfe isInitialized für non-nullable Properties
                try {
                    $isInitialized = $property->isInitialized($object);

                    if (! $isInitialized && ! $allowsNull) {
                        $result->addError(
                            $property->getName(),
                            sprintf("Feld '%s' darf nicht null sein.", $property->getName())
                        );
                    }
                } catch (\Throwable) {
                    // Fehler beim Lesen sowohl des Werts als auch der Initialisierung - überspringen
                }

                continue;
            }

            // Automatische Required-Validierung für non-nullable Properties
            if (! $allowsNull) {
                $propertyType = $property->getType();
                $isString = $propertyType instanceof \ReflectionNamedType && $propertyType->getName() === 'string';

                if ($isString && (empty($value) && $value !== '0')) {
                    $result->addError(
                        $propertyName,
                        sprintf("Feld '%s' ist erforderlich.", $propertyName)
                    );

                    // Keine weiteren Validierungen für dieses Property
                    continue;
                }
            }

            foreach ($property->getAttributes() as $attribute) {
                $attrInstance = $attribute->newInstance();


                if ($attrInstance instanceof ValidationRule) {
                    $shouldValidate = false;

                    if ($group === null) {
                        $shouldValidate = true;
                    } elseif ($attrInstance instanceof GroupAware) {
                        $shouldValidate = $attrInstance->belongsToGroup($group);
                    } else {
                        $shouldValidate = true;
                    }

                    if ($shouldValidate && ! $attrInstance->validate($value)) {
                        $result->addErrors($property->getName(), $attrInstance->getErrorMessages());
                    }
                }
            }

        }

        return $result;
    }
}
