<?php

namespace Tests\Feature;

use Illuminate\Support\Str;
use Mtc\Modules\BlockRegistry\Models\BlockRegistry;
use Mtc\Modules\BlockRegistry\Models\BlockTemplate;
use Mtc\Modules\BlockRegistry\Models\BlockTemplateItem;
use Mtc\Modules\BlockRegistry\Models\EntityBlock;
use Mtc\Modules\BlockRegistry\Services\BlockTemplateService;

class BlockTemplateServiceTest extends \PHPUnit\Framework\TestCase
{
    protected BlockTemplateService $service;

    /** @var BlockRegistry[] */
    private array $blocks = [];

    /** @var EntityBlock[] */
    private array $entityBlocks = [];

    /** @var BlockTemplate[] */
    private array $templates = [];

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

    protected function setUp(): void
    {
        parent::setUp();
        $this->service = app(BlockTemplateService::class);
        $this->createdEntityIds = [];
    }

    protected function tearDown(): void
    {
        foreach ($this->templates as $t) {
            BlockTemplateItem::where('block_template_id', $t->id)->delete();
            $t->delete();
        }
        foreach ($this->entityBlocks as $eb) {
            EntityBlock::where('id', $eb->id)->delete();
        }
        if (!empty($this->createdEntityIds)) {
            EntityBlock::where('entity_type', 'page')
                ->whereIn('entity_id', $this->createdEntityIds)
                ->delete();
        }
        foreach ($this->blocks as $b) {
            $b->delete();
        }
        parent::tearDown();
    }

    public function test_apply_template_creates_fresh_blocks_as_draft(): void
    {
        $blockA = $this->makeBlock('Hero');
        $blockB = $this->makeBlock('CTA');

        $template = $this->createTemplate('Fresh Only', [
            ['type' => 'block', 'id' => $blockA->id],
            ['type' => 'block', 'id' => $blockB->id],
        ]);

        $entityId = $this->fakeEntityId();
        $count = $this->service->applyTemplateToEntity($template->id, 'page', $entityId);

        $this->assertSame(2, $count);

        $created = EntityBlock::where('entity_type', 'page')
            ->where('entity_id', $entityId)
            ->orderBy('order')
            ->get();

        $this->assertCount(2, $created);
        foreach ($created as $row) {
            $this->assertSame(EntityBlock::STATUS_DRAFT, $row->status);
            $this->assertNull($row->source_block_id);
        }
        $this->assertSame([$blockA->id, $blockB->id], $created->pluck('block_id')->all());
    }

    public function test_apply_template_creates_global_refs_as_published(): void
    {
        $block = $this->makeBlock('Footer');
        $global = $this->makeGlobalBlock($block, 'Site Footer');

        $template = $this->createTemplate('Global Only', [
            ['type' => 'global', 'id' => $global->id],
        ]);

        $entityId = $this->fakeEntityId();
        $count = $this->service->applyTemplateToEntity($template->id, 'page', $entityId);

        $this->assertSame(1, $count);

        $created = EntityBlock::where('entity_type', 'page')
            ->where('entity_id', $entityId)
            ->first();

        $this->assertNotNull($created);
        $this->assertSame(EntityBlock::STATUS_PUBLISHED, $created->status);
        $this->assertSame($global->id, (int) $created->source_block_id);
        $this->assertSame($block->id, (int) $created->block_id);
    }

    public function test_apply_template_preserves_order_across_mixed_items(): void
    {
        $blockA = $this->makeBlock('Hero');
        $blockB = $this->makeBlock('CTA');
        $globalA = $this->makeGlobalBlock($blockA, 'Global Hero');
        $globalB = $this->makeGlobalBlock($blockB, 'Global CTA');

        $template = $this->createTemplate('Mixed', [
            ['type' => 'block',  'id' => $blockA->id],
            ['type' => 'global', 'id' => $globalA->id],
            ['type' => 'block',  'id' => $blockB->id],
            ['type' => 'global', 'id' => $globalB->id],
        ]);

        $entityId = $this->fakeEntityId();
        $count = $this->service->applyTemplateToEntity($template->id, 'page', $entityId);

        $this->assertSame(4, $count);

        $created = EntityBlock::where('entity_type', 'page')
            ->where('entity_id', $entityId)
            ->orderBy('order')
            ->get();

        $this->assertCount(4, $created);

        $this->assertNull($created[0]->source_block_id);
        $this->assertSame(EntityBlock::STATUS_DRAFT, $created[0]->status);
        $this->assertSame($blockA->id, (int) $created[0]->block_id);

        $this->assertSame($globalA->id, (int) $created[1]->source_block_id);
        $this->assertSame(EntityBlock::STATUS_PUBLISHED, $created[1]->status);
        $this->assertSame($blockA->id, (int) $created[1]->block_id);

        $this->assertNull($created[2]->source_block_id);
        $this->assertSame(EntityBlock::STATUS_DRAFT, $created[2]->status);
        $this->assertSame($blockB->id, (int) $created[2]->block_id);

        $this->assertSame($globalB->id, (int) $created[3]->source_block_id);
        $this->assertSame(EntityBlock::STATUS_PUBLISHED, $created[3]->status);
        $this->assertSame($blockB->id, (int) $created[3]->block_id);
    }

    public function test_apply_template_propagates_region(): void
    {
        $block = $this->makeBlock('Hero');

        $template = $this->createTemplate('Region Test', [
            ['type' => 'block', 'id' => $block->id],
        ]);

        $entityId = $this->fakeEntityId();
        $this->service->applyTemplateToEntity($template->id, 'page', $entityId, 'footer');

        $created = EntityBlock::where('entity_type', 'page')
            ->where('entity_id', $entityId)
            ->first();

        $this->assertNotNull($created);
        $this->assertSame('footer', $created->region);
    }

    public function test_sync_items_dedupes_duplicate_keys(): void
    {
        $block = $this->makeBlock('Hero');
        $global = $this->makeGlobalBlock($block, 'Site Hero');

        $template = $this->createTemplate('Dupes', [
            ['type' => 'block',  'id' => $block->id],
            ['type' => 'block',  'id' => $block->id],
            ['type' => 'global', 'id' => $global->id],
            ['type' => 'global', 'id' => $global->id],
        ]);

        $rows = BlockTemplateItem::where('block_template_id', $template->id)->get();

        $this->assertCount(2, $rows, 'syncItems should collapse duplicate (type, id) pairs');

        $keys = $rows->map(function ($row) {
            return ($row->source_block_id ? 'global:' . $row->source_block_id : 'block:' . $row->block_id);
        })->sort()->values()->all();

        $this->assertSame(
            ['block:' . $block->id, 'global:' . $global->id],
            $keys
        );
    }

    private function createTemplate(string $name, array $items): BlockTemplate
    {
        $uniqueName = $name . ' ' . Str::random(8);
        $template = $this->service->createTemplate([
            'name' => $uniqueName,
            'items' => $items,
        ]);
        $this->templates[] = $template;
        return $template;
    }

    private function makeBlock(string $name): BlockRegistry
    {
        $block = BlockRegistry::create([
            'uuid' => (string) Str::uuid(),
            'name' => $name,
            'slug' => 'test-' . strtolower($name) . '-' . Str::random(8),
            'icon' => 'fa-cube',
            'is_active' => true,
        ]);
        $this->blocks[] = $block;
        return $block;
    }

    private function makeGlobalBlock(BlockRegistry $block, string $name): EntityBlock
    {
        $global = EntityBlock::create([
            'entity_type' => 'global',
            'entity_id' => 0,
            'block_id' => $block->id,
            'name' => $name . ' ' . Str::random(8),
            'status' => EntityBlock::STATUS_PUBLISHED,
            'data' => [],
        ]);
        $this->entityBlocks[] = $global;
        return $global;
    }

    private function fakeEntityId(): int
    {
        $id = random_int(900000, 999999);
        $this->createdEntityIds[] = $id;
        return $id;
    }
}
