<?php

namespace Tests\Feature;

use Illuminate\Support\Str;
use Mtc\Modules\BlockRegistry\Models\BlockRegistry;
use Mtc\Modules\BlockRegistry\Models\EntityBlock;
use Mtc\Modules\BlockRegistry\Services\EntityBlockService;
use Mtc\Modules\BlockRegistry\Services\TemplateRegionRegistry;

class BlockRegistryRegionsTest extends \PHPUnit\Framework\TestCase
{
    private ?BlockRegistry $block = null;

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

    private string $entityType = 'page';

    /**
     * Use a high entity_id that's unlikely to collide with real fixtures so
     * forEntity scopes uniquely identify our test rows.
     */
    private int $entityId;

    protected function setUp(): void
    {
        parent::setUp();
        $this->entityId = random_int(900000, 999999);

        $slug = 'test-regions-' . Str::random(8);
        $this->block = BlockRegistry::create([
            'uuid' => (string) Str::uuid(),
            'name' => 'Region Test Block',
            'slug' => $slug,
            'is_active' => true,
        ]);
    }

    protected function tearDown(): void
    {
        foreach ($this->entityBlocks as $eb) {
            $eb->delete();
        }
        if ($this->block) {
            $this->block->delete();
        }
        parent::tearDown();
    }

    /**
     * Regions are independent ordering sequences: getMaxOrder per region
     * does not see other regions' rows.
     */
    public function testGetMaxOrderIsScopedPerRegion(): void
    {
        $this->makeBlock('header', 0);
        $this->makeBlock('header', 1);
        $this->makeBlock('main', 0);

        $this->assertSame(1, EntityBlock::getMaxOrder($this->entityType, $this->entityId, 'header'));
        $this->assertSame(0, EntityBlock::getMaxOrder($this->entityType, $this->entityId, 'main'));
        $this->assertSame(-1, EntityBlock::getMaxOrder($this->entityType, $this->entityId, 'footer'));
    }

    /**
     * Auto-assigned order on create is also scoped per region: a new block
     * in 'main' starts at 0 even when 'header' already has rows.
     */
    public function testAutoOrderOnCreateIsScopedPerRegion(): void
    {
        $h1 = $this->makeBlock('header', null);
        $h2 = $this->makeBlock('header', null);
        $m1 = $this->makeBlock('main', null);

        $this->assertSame(0, $h1->order);
        $this->assertSame(1, $h2->order);
        $this->assertSame(0, $m1->order, 'main region order should restart at 0');
    }

    /**
     * forRegion('main') and forRegion(null) both match legacy NULL rows
     * and explicit 'main' rows. Other regions only match their own slug.
     */
    public function testForRegionScopeMatchesLegacyNullAsMain(): void
    {
        $legacy = $this->makeBlock(null, 0);
        $main = $this->makeBlock('main', 1);
        $header = $this->makeBlock('header', 0);

        $mainRows = EntityBlock::forEntity($this->entityType, $this->entityId)
            ->forRegion('main')
            ->pluck('id')
            ->all();
        sort($mainRows);

        $expected = [$legacy->id, $main->id];
        sort($expected);

        $this->assertSame($expected, $mainRows, 'main region should match NULL + "main" rows');

        $headerRows = EntityBlock::forEntity($this->entityType, $this->entityId)
            ->forRegion('header')
            ->pluck('id')
            ->all();
        $this->assertSame([$header->id], $headerRows);
    }

    /**
     * cacheKey is region-aware: distinct keys per (entity, region) and a
     * separate "all" key when no region is given.
     */
    public function testCacheKeyIncludesRegion(): void
    {
        $base = EntityBlockService::cacheKey('page', 42);
        $main = EntityBlockService::cacheKey('page', 42, 'main');
        $header = EntityBlockService::cacheKey('page', 42, 'header');

        $this->assertNotSame($base, $main);
        $this->assertNotSame($main, $header);
        $this->assertSame('block_html:page:42:all', $base);
        $this->assertSame('block_html:page:42:main', $main);
        $this->assertSame('block_html:page:42:header', $header);
    }

    /**
     * TemplateRegionRegistry returns the declared regions for known
     * templates and falls back to a single 'main' region otherwise.
     */
    public function testTemplateRegionRegistryDefaults(): void
    {
        $this->assertSame(['main'], TemplateRegionRegistry::regionsFor(null));
        $this->assertSame(['main'], TemplateRegionRegistry::regionsFor('made_up_template.twig'));
        $this->assertFalse(TemplateRegionRegistry::hasMultipleRegions(null));

        $declared = TemplateRegionRegistry::regionsFor('content_sidebar.twig');
        $this->assertContains('main', $declared);
        $this->assertGreaterThan(1, count($declared));
        $this->assertTrue(TemplateRegionRegistry::hasMultipleRegions('content_sidebar.twig'));
    }

    /**
     * isValidRegion accepts null, accepts declared regions, rejects everything
     * else for a multi-region template.
     */
    public function testIsValidRegionRejectsUndeclared(): void
    {
        $this->assertTrue(TemplateRegionRegistry::isValidRegion('content_sidebar.twig', null));
        $this->assertTrue(TemplateRegionRegistry::isValidRegion('content_sidebar.twig', 'main'));
        $this->assertFalse(TemplateRegionRegistry::isValidRegion('content_sidebar.twig', 'sidebar'));
        $this->assertFalse(TemplateRegionRegistry::isValidRegion('content_sidebar.twig', 'header-x'));
    }

    private function makeBlock(?string $region, ?int $order): EntityBlock
    {
        $eb = EntityBlock::create([
            'entity_type' => $this->entityType,
            'entity_id' => $this->entityId,
            'block_id' => $this->block->id,
            'region' => $region,
            'order' => $order,
            'status' => EntityBlock::STATUS_PUBLISHED,
            'data' => [],
        ]);
        $this->entityBlocks[] = $eb;
        return $eb;
    }
}
