<?php

declare(strict_types=1);

namespace Ferienpass\AdminBundle\Tests\Form\TypeGuesser;

use Ferienpass\AdminBundle\Form\TypeGuesser\TypeGuesser;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Form\FormTypeInterface;
use Symfony\Component\Form\Guess\TypeGuess;

final class TypeGuesserTest extends TestCase
{
    private function makeType(): FormTypeInterface
    {
        return new class implements FormTypeInterface {
            public function getParent(): ?string { return null; }
            public function buildForm(\Symfony\Component\Form\FormBuilderInterface $builder, array $options): void {}
            public function buildView(\Symfony\Component\Form\FormView $view, \Symfony\Component\Form\FormInterface $form, array $options): void {}
            public function finishView(\Symfony\Component\Form\FormView $view, \Symfony\Component\Form\FormInterface $form, array $options): void {}
            public function configureOptions(\Symfony\Component\OptionsResolver\OptionsResolver $resolver): void {}
            public function getBlockPrefix(): string { return 'anon'; }
            public static function getExtendedType(): string { return 'text'; }
            public static function getExtendedTypes(): iterable { return []; }
        };
    }

    public function testGuessTypeMatchesBothClassAndProperty(): void
    {
        $type = $this->makeType();
        $guesser = new TypeGuesser($type, \stdClass::class, 'myProp');

        $guess = $guesser->guessType(\stdClass::class, 'myProp');

        self::assertNotNull($guess);
        self::assertSame($type::class, $guess->getType());
        self::assertSame(TypeGuess::VERY_HIGH_CONFIDENCE, $guess->getConfidence());
    }

    public function testGuessTypeMatchesPropertyOnlyWhenNoClassConstraint(): void
    {
        $type = $this->makeType();
        $guesser = new TypeGuesser($type, null, 'myProp');

        $guess = $guesser->guessType(\stdClass::class, 'myProp');

        self::assertNotNull($guess);
        self::assertSame(TypeGuess::HIGH_CONFIDENCE, $guess->getConfidence());
    }

    public function testGuessTypeReturnsNullWhenPropertyDoesNotMatch(): void
    {
        $type = $this->makeType();
        $guesser = new TypeGuesser($type, \stdClass::class, 'myProp');

        $guess = $guesser->guessType(\stdClass::class, 'otherProp');

        self::assertNull($guess);
    }

    public function testGuessTypeReturnsNullWhenClassDoesNotMatch(): void
    {
        $type = $this->makeType();
        $guesser = new TypeGuesser($type, \ArrayObject::class, 'myProp');

        $guess = $guesser->guessType(\stdClass::class, 'myProp');

        self::assertNull($guess);
    }

    public function testGuessTypeWorksForSubclass(): void
    {
        $type = $this->makeType();
        $guesser = new TypeGuesser($type, \Countable::class, 'myProp');

        // \ArrayObject implements \Countable
        $guess = $guesser->guessType(\ArrayObject::class, 'myProp');

        self::assertNotNull($guess);
        self::assertSame(TypeGuess::VERY_HIGH_CONFIDENCE, $guess->getConfidence());
    }

    public function testGuessRequiredAlwaysReturnsNull(): void
    {
        $guesser = new TypeGuesser($this->makeType(), null, 'prop');

        self::assertNull($guesser->guessRequired(\stdClass::class, 'prop'));
    }

    public function testGuessMaxLengthAlwaysReturnsNull(): void
    {
        $guesser = new TypeGuesser($this->makeType(), null, 'prop');

        self::assertNull($guesser->guessMaxLength(\stdClass::class, 'prop'));
    }

    public function testGuessPatternAlwaysReturnsNull(): void
    {
        $guesser = new TypeGuesser($this->makeType(), null, 'prop');

        self::assertNull($guesser->guessPattern(\stdClass::class, 'prop'));
    }
}
