<?php

declare(strict_types=1);

namespace Drupal\Tests\canvas\Kernel\Config;

use Drupal\Component\Utility\Crypt;
use Drupal\canvas\Entity\AssetLibrary;

/**
 * Tests validation of Asset Library entities.
 *
 * @group canvas
 */
class AssetLibraryValidationTest extends BetterConfigEntityValidationTestBase {

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

  /**
   * {@inheritdoc}
   *
   * @phpstan-ignore property.defaultValue
   */
  protected static array $propertiesWithRequiredKeys = [
    'css' => [
      "'original' is a required key.",
      "'compiled' is a required key.",
    ],
    'js' => [
      "'original' is a required key.",
      "'compiled' is a required key.",
    ],
  ];

  /**
   * {@inheritdoc}
   */
  protected static array $propertiesWithOptionalValues = [
    'css',
    'js',
  ];

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

    $this->entity = AssetLibrary::create([
      'id' => 'global',
      'label' => 'Test',
      'css' => [
        'original' => '.test { display: none; }',
        'compiled' => '.test{display:none;}',
      ],
      'js' => [
        'original' => 'console.log( "Test" )',
        'compiled' => 'console.log("Test")',
      ],
    ]);
    $this->entity->save();
  }

  public function testEntityAssets(): void {
    $css = $this->entity->get('css')['compiled'];
    $js = $this->entity->get('js')['compiled'];
    $css_hash = Crypt::hmacBase64($css, $this->entity->uuid());
    $js_hash = Crypt::hmacBase64($js, $this->entity->uuid());

    self::assertStringEqualsFile('assets://canvas/' . $css_hash . '.css', $css);
    self::assertStringEqualsFile('assets://canvas/' . $js_hash . '.js', $js);
  }

  /**
   * Tests different permutations of entity values.
   *
   * @param array $shape
   *   Array of entity values.
   * @param array $expected_errors
   *   Expected validation errors.
   *
   * @dataProvider providerTestEntityShapes
   */
  public function testEntityShapes(array $shape, array $expected_errors): void {
    $this->entity = AssetLibrary::create($shape);
    $this->assertValidationErrors($expected_errors);
  }

  public static function providerTestEntityShapes(): array {
    return [
      'Valid: no JS or CSS' => [
        [
          'id' => 'global',
          'label' => 'Test',
          'css' => NULL,
          'js' => NULL,
        ],
        [],
      ],
      'Invalid: compiled without source' => [
        [
          'id' => 'global',
          'label' => 'Test',
          'css' => [
            'compiled' => '.disgusting {color: puke-green;}',
          ],
          'js' => [
            'compiled' => 'console.log( "To paraphrase: The only source of compiled is original." )',
          ],
        ],
        [
          'css' => "'original' is a required key.",
          'js' => "'original' is a required key.",
        ],
      ],
      'Invalid: source without compiled' => [
        [
          'id' => 'global',
          'label' => 'Test',
          'css' => [
            'original' => '.gracie-dog { color: black-brown; }',
          ],
          'js' => [
            'original' => 'console.log( "I am the source of nothing!" )',
          ],
        ],
        [
          'css' => "'compiled' is a required key.",
          'js' => "'compiled' is a required key.",
        ],
      ],
      'Invalid: incorrect key under css and js' => [
        [
          'id' => 'global',
          'label' => 'Test',
          'css' => [
            'snazzy_css' => '.test { opacity: 30; }',
          ],
          'js' => [
            'snazzy_js' => 'console.log( "🎤Is this thing on?" )',
          ],
        ],
        [
          'css' => [
            "'original' is a required key.",
            "'compiled' is a required key.",
          ],
          'css.snazzy_css' => "'snazzy_css' is not a supported key.",
          'js' => [
            "'original' is a required key.",
            "'compiled' is a required key.",
          ],
          'js.snazzy_js' => "'snazzy_js' is not a supported key.",
        ],
      ],
    ];
  }

  /**
   * @dataProvider providerInvalidMachineNameCharacters
   */
  public function testInvalidMachineNameCharacters(string $machine_name, bool $is_expected_to_be_valid): void {
    // @todo Change the autogenerated stub
    parent::testInvalidMachineNameCharacters($machine_name, $is_expected_to_be_valid);
  }

  public static function providerInvalidMachineNameCharacters(): array {
    $cases = parent::providerInvalidMachineNameCharacters();
    unset($cases['VALID: underscore separated']);
    return $cases + [
      'INVALID: not global' => ['not_global', FALSE],
    ];
  }

  /**
   * @see ::testImmutableProperties
   *
   * Due to allowing only a single config entity, we need to do something extra
   * special to allow this test to pass.
   */
  protected function assertValidationErrors(array $expected_messages): void {
    if ($expected_messages === ['' => "The 'id' property cannot be changed."]) {
      $expected_messages['id'] = 'The <em class="placeholder">&quot;something&quot;</em> machine name is not valid.';
    }
    parent::assertValidationErrors($expected_messages);
  }

  public function testMachineNameLength(string $prefix = ''): void {
    // \Drupal\KernelTests\Core\Config\ConfigEntityValidationTestBase::testMachineNameLength()
    // does not allow overriding the expected message for an invalid machine
    // name. Since we only allow 1 possible value it seems reasonable to skip this
    // test until we support other machine names.
    $this->markTestSkipped("Currently the only allowed value for the machine name is 'global'.");
  }

  public function testImmutableProperties(array $valid_values = []): void {
    $valid_values['id'] = 'something';
    parent::testImmutableProperties($valid_values);
  }

}
