<?php

declare(strict_types=1);

/*
 * This file is part of the RegexParser package.
 *
 * (c) Younes ENNAJI <younes.ennaji.pro@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace RegexParser\Tests\Unit\NodeVisitor;

use PHPUnit\Framework\TestCase;
use RegexParser\Node\AlternationNode;
use RegexParser\Node\AssertionNode;
use RegexParser\Node\CharClassNode;
use RegexParser\Node\CharTypeNode;
use RegexParser\Node\LiteralNode;
use RegexParser\Node\QuantifierNode;
use RegexParser\Node\QuantifierType;
use RegexParser\Node\RangeNode;
use RegexParser\Node\UnicodeNode;
use RegexParser\NodeVisitor\TestCaseGeneratorNodeVisitor;

final class TestCaseGeneratorNodeVisitorCoverageTest extends TestCase
{
    public function test_visit_assertion_returns_empty_cases(): void
    {
        $visitor = new TestCaseGeneratorNodeVisitor();
        $cases = (new AssertionNode('A', 0, 0))->accept($visitor);

        $this->assertSame([''], $cases['matching']);
        $this->assertSame([''], $cases['non_matching']);
    }

    public function test_visit_char_class_empty_parts_returns_non_matching(): void
    {
        $visitor = new TestCaseGeneratorNodeVisitor();
        $emptyAlt = new AlternationNode([], 0, 0);
        $class = new CharClassNode($emptyAlt, false, 0, 0);

        $cases = $class->accept($visitor);

        $this->assertSame([], $cases['matching']);
        $this->assertSame(['a'], $cases['non_matching']);
    }

    public function test_visit_range_with_non_literal_bounds_returns_defaults(): void
    {
        $visitor = new TestCaseGeneratorNodeVisitor();
        $range = new RangeNode(new CharTypeNode('d', 0, 0), new LiteralNode('z', 0, 0), 0, 0);

        $cases = $range->accept($visitor);

        $this->assertSame(['a'], $cases['matching']);
        $this->assertSame(['!'], $cases['non_matching']);
    }

    public function test_visit_unicode_returns_basic_cases(): void
    {
        $visitor = new TestCaseGeneratorNodeVisitor();
        $cases = (new UnicodeNode('\\x{41}', 0, 0))->accept($visitor);

        $this->assertSame(['a'], $cases['matching']);
        $this->assertSame(['!'], $cases['non_matching']);
    }

    public function test_quantifier_with_max_adds_non_matching_sample(): void
    {
        $visitor = new TestCaseGeneratorNodeVisitor();
        $node = new QuantifierNode(new LiteralNode('a', 0, 0), '{1,2}', QuantifierType::T_GREEDY, 0, 0);

        $cases = $node->accept($visitor);

        $this->assertContains('aaa', $cases['non_matching']);
    }

    public function test_parse_quantifier_range_variants(): void
    {
        $visitor = new TestCaseGeneratorNodeVisitor();
        $method = (new \ReflectionClass($visitor))->getMethod('parseQuantifierRange');

        $this->assertSame([0, null], $method->invoke($visitor, '*'));
        $this->assertSame([1, null], $method->invoke($visitor, '+'));
        $this->assertSame([0, 1], $method->invoke($visitor, '?'));
        $this->assertSame([2, null], $method->invoke($visitor, '{2,}'));
        $this->assertSame([2, 3], $method->invoke($visitor, '{2,3}'));
        $this->assertSame([2, 2], $method->invoke($visitor, '{2}'));
    }

    public function test_generate_for_char_type_variants(): void
    {
        $visitor = new TestCaseGeneratorNodeVisitor();
        $method = (new \ReflectionClass($visitor))->getMethod('generateForCharType');

        $this->assertSame('a', $method->invoke($visitor, 'S'));
        $this->assertSame('a', $method->invoke($visitor, 'w'));
        $this->assertSame('!', $method->invoke($visitor, 'W'));
        $this->assertSame(' ', $method->invoke($visitor, 'h'));
        $this->assertSame('a', $method->invoke($visitor, 'H'));
        $this->assertSame("\n", $method->invoke($visitor, 'v'));
        $this->assertSame('a', $method->invoke($visitor, 'V'));
        $this->assertSame("\r\n", $method->invoke($visitor, 'R'));
    }
}
