<?php

namespace Mtc\Plugins\BlockManager\Tests\Feature;

use Illuminate\Support\Str;
use Mtc\Modules\BlockRegistry\Models\BlockRegistry;
use Mtc\Plugins\BlockManager\Models\BlockManagerBlock;
use Tests\TestCase;

class ShipApiFieldsPanelTest extends TestCase
{
    /** @var string[] */
    private array $createdUuids = [];

    /** @var string[] */
    private array $shippedSlugs = [];

    protected function tearDown(): void
    {
        $twigDir = base_path('sites/default/templates/includes/page_blocks');
        $jsonDir = base_path('modules/BlockRegistry/blocks');

        foreach ($this->shippedSlugs as $slug) {
            @unlink($twigDir . "/{$slug}.twig");
            @unlink($jsonDir . "/{$slug}.json");
            BlockRegistry::where('slug', $slug)->delete();
        }
        if (!empty($this->createdUuids)) {
            BlockManagerBlock::whereIn('uuid', $this->createdUuids)->delete();
        }
        parent::tearDown();
    }

    public function test_ship_uses_auto_derived_fields_when_panel_absent(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
                ['instance_id' => 'el-002', 'element_type' => 'button',  'variant' => 'primary', 'field_name' => 'cta'],
            ],
        ]);

        $json = $this->shipAndReadJson($block);
        $names = array_column($json['fields'], 'name');

        foreach (['title_text', 'title_level', 'cta_label', 'cta_link', 'cta_target'] as $expected) {
            $this->assertContains(
                $expected,
                $names,
                "Auto-derived field '{$expected}' should be present when no fields_panel is set."
            );
        }
    }

    public function test_ship_applies_renames_from_panel(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
            ],
            'fields_panel' => [
                'renames' => ['title_text' => 'title'],
            ],
        ]);

        $json = $this->shipAndReadJson($block);
        $names = array_column($json['fields'], 'name');

        $this->assertContains('title', $names, "Renamed field 'title' should be present.");
        $this->assertNotContains('title_text', $names, "Auto-derived 'title_text' should be replaced by the rename.");
    }

    public function test_ship_reorders_fields_from_panel(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
            ],
            'fields_panel' => [
                'order' => ['title_level', 'title_text'],
            ],
        ]);

        $json = $this->shipAndReadJson($block);
        $names = array_column($json['fields'], 'name');

        $this->assertSame(
            'title_level',
            $names[0],
            "First field should be 'title_level' per fields_panel.order."
        );
        $this->assertSame(
            'title_text',
            $names[1],
            "Second field should be 'title_text' per fields_panel.order."
        );
    }

    public function test_ship_overrides_required_from_panel(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
            ],
            'fields_panel' => [
                // heading.text manifest says required: true → flip to false.
                // heading.level manifest says required: false → flip to true.
                'required' => ['title_text' => false, 'title_level' => true],
            ],
        ]);

        $json = $this->shipAndReadJson($block);
        $byName = [];
        foreach ($json['fields'] as $f) {
            $byName[$f['name']] = $f;
        }

        $this->assertFalse((bool) $byName['title_text']['required'], "title_text required should be flipped to false.");
        $this->assertTrue((bool) $byName['title_level']['required'], "title_level required should be flipped to true.");
    }

    public function test_ship_appends_custom_fields_from_panel(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
            ],
            'fields_panel' => [
                'custom' => [
                    ['name' => 'section_toggle', 'label' => 'Toggle', 'type' => 'checkbox', 'required' => false],
                ],
            ],
        ]);

        $json = $this->shipAndReadJson($block);
        $byName = [];
        foreach ($json['fields'] as $f) {
            $byName[$f['name']] = $f;
        }

        $this->assertArrayHasKey('section_toggle', $byName, "Custom field 'section_toggle' should appear in JSON.");
        $this->assertSame('checkbox', $byName['section_toggle']['type']);
    }

    public function test_ship_groups_instances_as_repeater_from_panel(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'paragraph', 'variant' => 'default', 'field_name' => 'slide_a'],
                ['instance_id' => 'el-002', 'element_type' => 'paragraph', 'variant' => 'default', 'field_name' => 'slide_b'],
            ],
            'fields_panel' => [
                'repeaters' => [
                    [
                        'name'          => 'slides',
                        'label'         => 'Slides',
                        'instance_ids'  => ['el-001', 'el-002'],
                        'singular'      => 'slide',
                        'repeater_min'  => 1,
                        'repeater_max'  => 10,
                    ],
                ],
            ],
        ]);

        $json = $this->shipAndReadJson($block);
        $byName = [];
        foreach ($json['fields'] as $f) {
            $byName[$f['name']] = $f;
        }

        $this->assertArrayHasKey('slides', $byName, "Repeater field 'slides' should appear in JSON.");
        $this->assertSame('repeater', $byName['slides']['type']);
        $this->assertIsArray($byName['slides']['sub_fields'] ?? null, "Repeater must declare sub_fields[].");
        $this->assertGreaterThanOrEqual(
            2,
            count($byName['slides']['sub_fields']),
            "Both grouped paragraph instances' fields should be sub_fields of the repeater."
        );

        // Grouped instance fields must NOT also appear at the top level.
        $topNames = array_column($json['fields'], 'name');
        $this->assertNotContains('slide_a_text', $topNames, "Grouped instance fields should not be top-level.");
        $this->assertNotContains('slide_b_text', $topNames, "Grouped instance fields should not be top-level.");
    }

    public function test_ship_twig_uses_renamed_field_names(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
            ],
            'fields_panel' => [
                'renames' => ['title_text' => 'title'],
            ],
        ]);

        $this->shipBlock($block);
        $twig = file_get_contents(base_path("sites/default/templates/includes/page_blocks/{$block->slug}.twig"));

        $this->assertStringContainsString('fields.title', $twig, "Twig should bind to renamed 'fields.title'.");
        $this->assertStringNotContainsString('fields.title_text', $twig, "Twig should no longer bind to 'fields.title_text'.");
    }

    public function test_ship_twig_wraps_repeater_in_for_loop(): void
    {
        $block = $this->seedBlock([
            'elements' => [
                ['instance_id' => 'el-001', 'element_type' => 'paragraph', 'variant' => 'default', 'field_name' => 'slide_a'],
                ['instance_id' => 'el-002', 'element_type' => 'paragraph', 'variant' => 'default', 'field_name' => 'slide_b'],
            ],
            'fields_panel' => [
                'repeaters' => [
                    ['name' => 'slides', 'instance_ids' => ['el-001', 'el-002'], 'singular' => 'slide'],
                ],
            ],
        ]);

        $this->shipBlock($block);
        $twig = file_get_contents(base_path("sites/default/templates/includes/page_blocks/{$block->slug}.twig"));

        $this->assertMatchesRegularExpression(
            '/\{%\s*for\s+slide\s+in\s+slides\s*%\}/',
            $twig,
            "Twig must open a {% for slide in slides %} block."
        );
        $this->assertStringContainsString('{% endfor %}', $twig, "Twig must close the for loop with {% endfor %}.");
        $this->assertStringContainsString('slide.text', $twig, "Bindings inside the loop must use 'slide.text'.");
        $this->assertStringNotContainsString('fields.slide_a_text', $twig, "Grouped instance bindings must not use the top-level 'fields.*' form.");
    }

    // ---- helpers ----

    private function seedBlock(array $structure): BlockManagerBlock
    {
        $uuid = (string) Str::uuid();
        $this->createdUuids[] = $uuid;

        return BlockManagerBlock::create([
            'uuid'           => $uuid,
            'slug'           => 'phase5-' . Str::random(8),
            'name'           => 'Phase 5 Probe',
            'description'    => 'Auto-seeded by ShipApiFieldsPanelTest.',
            'icon'           => 'fa-cube',
            'structure_json' => $structure,
        ]);
    }

    private function shipBlock(BlockManagerBlock $block): void
    {
        $response = $this->postJson("/api/block-manager/{$block->id}/ship");
        $response->assertStatus(200);
        $this->shippedSlugs[] = $block->slug;
    }

    private function shipAndReadJson(BlockManagerBlock $block): array
    {
        $this->shipBlock($block);
        $path = base_path("modules/BlockRegistry/blocks/{$block->slug}.json");
        $this->assertFileExists($path);
        $decoded = json_decode((string) file_get_contents($path), true);
        $this->assertIsArray($decoded);
        return $decoded;
    }
}
