<?php

declare(strict_types=1);

namespace Drupal\Tests\block\Kernel;

use Drupal\block\Entity\Block;
use Drupal\block\Hook\BlockHooks;
use Drupal\Core\Extension\ThemeHandlerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\KernelTests\KernelTestBase;
use Drupal\Tests\block\Traits\BlockCreationTrait;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;

/**
 * Tests block_rebuild().
 */
#[Group('block')]
#[RunTestsInSeparateProcesses]
class BlockRebuildTest extends KernelTestBase {

  use BlockCreationTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = ['block', 'system'];

  /**
   * {@inheritdoc}
   */
  protected static $configSchemaCheckerExclusions = [
    // These blocks are intentionally put into invalid regions, so they will
    // violate config schema.
    // @see ::testRebuildInvalidBlocks()
    'block.block.invalid_block1',
    'block.block.invalid_block2',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $this->container->get('theme_installer')->install(['stark']);
    $this->container->get('config.factory')->getEditable('system.theme')->set('default', 'stark')->save();
  }

  /**
   * Tests rebuild no blocks.
   *
   * @legacy-covers \Drupal\block\Hook\BlockHooks::rebuild
   */
  public function testRebuildNoBlocks(): void {
    $blockRebuild = \Drupal::service(BlockHooks::class);
    $blockRebuild->rebuild();
    $messages = \Drupal::messenger()->all();
    \Drupal::messenger()->deleteAll();
    $this->assertEquals([], $messages);
  }

  /**
   * Tests rebuild no invalid blocks.
   *
   * @legacy-covers \Drupal\block\Hook\BlockHooks::rebuild
   */
  public function testRebuildNoInvalidBlocks(): void {
    $this->placeBlock('system_powered_by_block', ['region' => 'content']);

    $blockRebuild = \Drupal::service(BlockHooks::class);
    $blockRebuild->rebuild();
    $messages = \Drupal::messenger()->all();
    \Drupal::messenger()->deleteAll();
    $this->assertEquals([], $messages);
  }

  /**
   * Tests rebuild invalid blocks.
   *
   * @legacy-covers \Drupal\block\Hook\BlockHooks::rebuild
   */
  public function testRebuildInvalidBlocks(): void {
    $this->placeBlock('system_powered_by_block', ['region' => 'content']);
    $block1 = $this->placeBlock('system_powered_by_block', [
      'id' => 'invalid_block1',
    ]);
    $block2 = $this->placeBlock('system_powered_by_block', [
      'id' => 'invalid_block2',
    ]);
    $block2->disable()->save();
    // Use the config API directly to bypass Block::preSave().
    \Drupal::configFactory()->getEditable('block.block.' . $block1->id())->set('region', 'INVALID')->save();
    \Drupal::configFactory()->getEditable('block.block.' . $block2->id())->set('region', 'INVALID')->save();

    // Reload block entities.
    $block1 = Block::load($block1->id());
    $block2 = Block::load($block2->id());

    $this->assertSame('INVALID', $block1->getRegion());
    $this->assertTrue($block1->status());
    $this->assertSame('INVALID', $block2->getRegion());
    $this->assertFalse($block2->status());

    $blockRebuild = \Drupal::service(BlockHooks::class);
    $blockRebuild->rebuild();

    // Reload block entities.
    $block1 = Block::load($block1->id());
    $block2 = Block::load($block2->id());

    $messages = \Drupal::messenger()->all();
    \Drupal::messenger()->deleteAll();
    $expected = [
      'warning' => [
        new TranslatableMarkup('The block %info was assigned to the invalid region %region and has been disabled.', [
          '%info' => $block1->id(),
          '%region' => 'INVALID',
        ]),
      ],
    ];
    $this->assertEquals($expected, $messages);

    $default_region = \Drupal::service(ThemeHandlerInterface::class)->getTheme('stark')->getDefaultRegion();
    $this->assertSame($default_region, $block1->getRegion());
    $this->assertFalse($block1->status());
    $this->assertSame($default_region, $block2->getRegion());
    $this->assertFalse($block2->status());
  }

}
