<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_memory\Unit\Plugin\MemoryBackend;

use Drupal\flowdrop_memory\Plugin\MemoryBackend\AbstractMemoryBackend;
use Drupal\Tests\UnitTestCase;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Tests AbstractMemoryBackend key-building helpers.
 *
 * Uses an anonymous concrete subclass to expose the protected helper methods.
 *
 * @coversDefaultClass \Drupal\flowdrop_memory\Plugin\MemoryBackend\AbstractMemoryBackend
 * @group flowdrop_memory
 */
class AbstractMemoryBackendTest extends UnitTestCase {

  /**
   * A testable concrete subclass exposing protected helpers.
   *
   * @var object
   */
  private object $backend;

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

    // Anonymous class exposing the protected key-builder methods.
    $this->backend = new class([], 'test', ['id' => 'test']) extends AbstractMemoryBackend {

      /**
       * {@inheritdoc}
       */
      public function get(string $scope, string $scope_id, string $key, mixed $default = NULL): mixed {
        return $default;
      }

      /**
       * {@inheritdoc}
       */
      public function set(string $scope, string $scope_id, string $key, mixed $value, ?int $ttl = NULL): void {}

      /**
       * {@inheritdoc}
       */
      public function delete(string $scope, string $scope_id, string $key): void {}

      /**
       * {@inheritdoc}
       */
      public function has(string $scope, string $scope_id, string $key): bool {
        return FALSE;
      }

      /**
       * {@inheritdoc}
       */
      public function keys(string $scope, string $scope_id): array {
        return [];
      }

      /**
       * {@inheritdoc}
       */
      public function clearScope(string $scope, string $scope_id): void {}

      /**
       * {@inheritdoc}
       */
      public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
        return new static($configuration, $plugin_id, $plugin_definition);
      }

      /**
       * Expose protected methods for testing.
       */
      public function publicBuildStorageKey(string $scope, string $scope_id, string $key): string {
        return $this->buildStorageKey($scope, $scope_id, $key);
      }

      /**
       * Expose buildScopePrefix for testing.
       */
      public function publicBuildScopePrefix(string $scope, string $scope_id): string {
        return $this->buildScopePrefix($scope, $scope_id);
      }

    };
  }

  /**
   * @covers ::buildStorageKey
   */
  public function testBuildStorageKeyWithExplicitScopeId(): void {
    $key = $this->backend->publicBuildStorageKey('workflow', 'wf-123', 'counter');
    $this->assertSame('flowdrop_memory:workflow:wf-123:counter', $key);
  }

  /**
   * @covers ::buildStorageKey
   */
  public function testBuildStorageKeyWithEmptyScopeIdUsesGlobalPlaceholder(): void {
    $key = $this->backend->publicBuildStorageKey('global', '', 'mykey');
    $this->assertSame('flowdrop_memory:global:__global__:mykey', $key);
  }

  /**
   * @covers ::buildStorageKey
   */
  public function testBuildStorageKeyUsesAllFourSegments(): void {
    $key = $this->backend->publicBuildStorageKey('pipeline', 'p-456', 'some_key');
    $parts = explode(':', $key);
    $this->assertCount(4, $parts);
    $this->assertSame('flowdrop_memory', $parts[0]);
    $this->assertSame('pipeline', $parts[1]);
    $this->assertSame('p-456', $parts[2]);
    $this->assertSame('some_key', $parts[3]);
  }

  /**
   * @covers ::buildStorageKey
   */
  public function testBuildStorageKeyIsUniquePerScopeId(): void {
    $key1 = $this->backend->publicBuildStorageKey('workflow', 'wf-1', 'x');
    $key2 = $this->backend->publicBuildStorageKey('workflow', 'wf-2', 'x');
    $this->assertNotSame($key1, $key2);
  }

  /**
   * @covers ::buildStorageKey
   */
  public function testBuildStorageKeyIsUniquePerScope(): void {
    $key1 = $this->backend->publicBuildStorageKey('workflow', 'same', 'x');
    $key2 = $this->backend->publicBuildStorageKey('pipeline', 'same', 'x');
    $this->assertNotSame($key1, $key2);
  }

  /**
   * @covers ::buildStorageKey
   */
  public function testBuildStorageKeyIsUniquePerKey(): void {
    $key1 = $this->backend->publicBuildStorageKey('global', '', 'key_a');
    $key2 = $this->backend->publicBuildStorageKey('global', '', 'key_b');
    $this->assertNotSame($key1, $key2);
  }

  /**
   * @covers ::buildScopePrefix
   */
  public function testBuildScopePrefixWithExplicitScopeId(): void {
    $prefix = $this->backend->publicBuildScopePrefix('pipeline', 'p-456');
    $this->assertSame('flowdrop_memory:pipeline:p-456', $prefix);
  }

  /**
   * @covers ::buildScopePrefix
   */
  public function testBuildScopePrefixWithEmptyScopeIdUsesGlobalPlaceholder(): void {
    $prefix = $this->backend->publicBuildScopePrefix('global', '');
    $this->assertSame('flowdrop_memory:global:__global__', $prefix);
  }

  /**
   * @covers ::buildScopePrefix
   */
  public function testBuildScopePrefixIsConsistentWithStorageKey(): void {
    // The scope prefix should be a prefix of any key built for the same scope.
    $prefix = $this->backend->publicBuildScopePrefix('workflow', 'wf-1');
    $key = $this->backend->publicBuildStorageKey('workflow', 'wf-1', 'some_key');
    $this->assertStringStartsWith($prefix . ':', $key);
  }

}
