<?php

namespace Mtc\Plugins\BlockManager\Tests\Unit;

use Illuminate\Support\Str;
use Mtc\Plugins\BlockManager\Models\BlockManagerBlock;
use Mtc\Plugins\BlockManager\Services\TwigGenerator;
use Tests\TestCase;
use Twig\Environment;
use Twig\Loader\ArrayLoader;

class TwigGeneratorTest extends TestCase
{
    private TwigGenerator $generator;

    protected function setUp(): void
    {
        parent::setUp();
        $this->generator = app(TwigGenerator::class);
    }

    public function test_generates_one_include_per_element_instance(): void
    {
        $block = $this->makeBlock([
            ['instance_id' => 'el-1', 'element_type' => 'heading',   'variant' => 'default', 'field_name' => 'title'],
            ['instance_id' => 'el-2', 'element_type' => 'paragraph', 'variant' => 'default', 'field_name' => 'body'],
            ['instance_id' => 'el-3', 'element_type' => 'button',    'variant' => 'primary', 'field_name' => 'cta'],
        ]);

        $output = $this->generator->generate($block);

        $this->assertSame(
            3,
            substr_count($output, '{% include'),
            'Expected one {% include %} per element instance.'
        );
    }

    public function test_include_path_matches_element_variant_convention(): void
    {
        $block = $this->makeBlock([
            ['instance_id' => 'el-1', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
            ['instance_id' => 'el-2', 'element_type' => 'button',  'variant' => 'primary', 'field_name' => 'cta'],
        ]);

        $output = $this->generator->generate($block);

        $this->assertStringContainsString('heading/variants/default.twig', $output);
        $this->assertStringContainsString('button/variants/primary.twig', $output);
    }

    public function test_include_emits_field_bindings_for_each_element_input(): void
    {
        $block = $this->makeBlock([
            ['instance_id' => 'el-1', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
        ]);

        $output = $this->generator->generate($block);

        // Heading manifest declares inputs 'text' and 'level'. The generated
        // `with { ... }` must bind both — we don't pin the right-hand-side
        // field-name scheme, only that each input appears as a key.
        $this->assertMatchesRegularExpression(
            '/\btext\s*:/',
            $output,
            "Generated include did not bind heading input 'text'."
        );
        $this->assertMatchesRegularExpression(
            '/\blevel\s*:/',
            $output,
            "Generated include did not bind heading input 'level'."
        );
    }

    public function test_generates_parseable_template_when_structure_has_no_elements(): void
    {
        $block = $this->makeBlock([]);
        $output = $this->generator->generate($block);

        $this->assertIsString($output);

        // Smoke: Twig must accept the output without throwing.
        $twig = new Environment(new ArrayLoader(['probe' => $output]));
        $rendered = $twig->render('probe', []);
        $this->assertIsString($rendered);
    }

    /**
     * @param array<int, array<string, mixed>> $elements
     */
    private function makeBlock(array $elements): BlockManagerBlock
    {
        // Unsaved instance is fine — the generator only reads structure_json.
        $block = new BlockManagerBlock([
            'uuid'           => (string) Str::uuid(),
            'slug'           => 'twiggen-probe-' . Str::random(6),
            'name'           => 'TwigGenerator probe',
            'structure_json' => ['elements' => $elements],
        ]);

        // Ensure the cast is applied as if the model came from DB.
        $block->setRawAttributes($block->getAttributes(), true);
        $block->setAttribute('structure_json', ['elements' => $elements]);

        return $block;
    }
}
