<?php

namespace Mtc\Plugins\BlockManager\Tests\Feature;

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

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

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

    /** @var int[] */
    private array $entityBlockIds = [];

    protected function tearDown(): void
    {
        if (!empty($this->entityBlockIds)) {
            EntityBlock::whereIn('id', $this->entityBlockIds)->delete();
        }
        $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_preview_returns_404_for_unknown_id(): void
    {
        $this->getJson('/api/block-manager/99999999/ship/preview')->assertStatus(404);
    }

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

        $response = $this->getJson("/api/block-manager/{$block->id}/ship/preview");
        $response->assertStatus(200);

        $data = $response->json('data');
        $this->assertFalse((bool) ($data['shipped'] ?? true), "Block has not been shipped yet.");
        $addedNames = array_column($data['fields_diff']['added'] ?? [], 'name');

        $this->assertContains('title_text', $addedNames);
        $this->assertContains('title_level', $addedNames);
        $this->assertSame([], $data['fields_diff']['removed'] ?? null, "Removed should be empty for an unshipped block.");
        $this->assertSame([], $data['fields_diff']['renamed'] ?? null, "Renamed should be empty for an unshipped block.");
        $this->assertSame(0, (int) ($data['entity_block_usage'] ?? -1));
    }

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

        $data = $this->getJson("/api/block-manager/{$block->id}/ship/preview")->json('data');

        $this->assertTrue((bool) ($data['shipped'] ?? false));
        $this->assertSame([], $data['fields_diff']['added'] ?? null);
        $this->assertSame([], $data['fields_diff']['removed'] ?? null);
        $this->assertSame([], $data['fields_diff']['renamed'] ?? null);
    }

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

        // Replace the heading instance with a paragraph
        $this->putJson("/api/block-manager/{$block->id}", [
            'structure_json' => [
                'elements' => [
                    ['instance_id' => 'el-2', 'element_type' => 'paragraph', 'variant' => 'default', 'field_name' => 'body'],
                ],
            ],
        ])->assertStatus(200);

        $data = $this->getJson("/api/block-manager/{$block->id}/ship/preview")->json('data');
        $addedNames   = array_column($data['fields_diff']['added']   ?? [], 'name');
        $removedNames = array_column($data['fields_diff']['removed'] ?? [], 'name');

        $this->assertContains('body_text', $addedNames, "Paragraph instance's field should be added.");
        $this->assertContains('title_text', $removedNames, "Heading's field should be removed.");
        $this->assertContains('title_level', $removedNames);
    }

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

        // Add a rename in fields_panel
        $this->putJson("/api/block-manager/{$block->id}", [
            'structure_json' => [
                'elements' => [
                    ['instance_id' => 'el-1', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
                ],
                'fields_panel' => ['renames' => ['title_text' => 'title']],
            ],
        ])->assertStatus(200);

        $data = $this->getJson("/api/block-manager/{$block->id}/ship/preview")->json('data');
        $renamed = $data['fields_diff']['renamed'] ?? [];

        $found = false;
        foreach ($renamed as $entry) {
            if (($entry['from'] ?? null) === 'title_text' && ($entry['to'] ?? null) === 'title') {
                $found = true;
                break;
            }
        }
        $this->assertTrue($found, "Preview must report rename {from: 'title_text', to: 'title'}.");
    }

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

        $registered = BlockRegistry::where('slug', $block->slug)->firstOrFail();

        // Seed an EntityBlock placement using the soon-to-be-renamed field.
        $entityBlock = EntityBlock::create([
            'entity_type' => 'page',
            'entity_id'   => 99999,
            'block_id'    => $registered->id,
            'order'       => 0,
            'status'      => 'draft',
            'data'        => ['title_text' => 'Hello world', 'title_level' => 'h1'],
        ]);
        $this->entityBlockIds[] = $entityBlock->id;

        // Stage a rename that will remove `title_text` from BlockRegistry.
        $this->putJson("/api/block-manager/{$block->id}", [
            'structure_json' => [
                'elements' => [
                    ['instance_id' => 'el-1', 'element_type' => 'heading', 'variant' => 'default', 'field_name' => 'title'],
                ],
                'fields_panel' => ['renames' => ['title_text' => 'title']],
            ],
        ])->assertStatus(200);

        $data = $this->getJson("/api/block-manager/{$block->id}/ship/preview")->json('data');

        $this->assertGreaterThanOrEqual(
            1,
            (int) ($data['entity_block_usage'] ?? 0),
            "Preview must count at least one EntityBlock placement at risk from the rename."
        );
    }

    // ---- helpers ----

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

        return BlockManagerBlock::create([
            'uuid'           => $uuid,
            'slug'           => 'phase6-prev-' . Str::random(8),
            'name'           => 'Phase6 Preview Probe',
            'icon'           => 'fa-cube',
            'structure_json' => $structure ?? ['elements' => []],
        ]);
    }

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