<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_runtime\Unit\Service\Storage;

use Drupal\Component\Uuid\UuidInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\flowdrop_runtime\Service\Storage\InMemoryPipeline;
use Drupal\flowdrop_runtime\Service\Storage\InMemoryPipelineStorage;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * Tests InMemoryPipelineStorage and InMemoryPipeline.
 *
 * @coversDefaultClass \Drupal\flowdrop_runtime\Service\Storage\InMemoryPipelineStorage
 * @group flowdrop_runtime
 */
class InMemoryPipelineStorageTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * Returns a storage instance backed by a predictable UUID stub.
   */
  private function createStorage(string $fixedUuid = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'): InMemoryPipelineStorage {
    $uuid = $this->prophesize(UuidInterface::class);
    $uuid->generate()->willReturn($fixedUuid);
    return new InMemoryPipelineStorage($uuid->reveal());
  }

  // ---------------------------------------------------------------------------
  // create()
  // ---------------------------------------------------------------------------

  /**
   * @covers ::create
   */
  public function testCreateReturnsInMemoryPipeline(): void {
    $storage = $this->createStorage();
    $pipeline = $storage->create(['workflow_id' => 'wf1']);

    $this->assertInstanceOf(InMemoryPipeline::class, $pipeline);
  }

  /**
   * @covers ::create
   */
  public function testCreateAssignsUniqueId(): void {
    $storage = $this->createStorage('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee');
    $pipeline = $storage->create([]);

    $this->assertSame('inmem_aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', $pipeline->id());
  }

  /**
   * @covers ::create
   */
  public function testCreateAssignsProperUuid(): void {
    $storage = $this->createStorage('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee');
    $pipeline = $storage->create([]);

    $this->assertSame('aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee', $pipeline->uuid());
  }

  // ---------------------------------------------------------------------------
  // save() / load()
  // ---------------------------------------------------------------------------

  /**
   * @covers ::save
   * @covers ::load
   */
  public function testSaveAndLoadRoundTrip(): void {
    $storage = $this->createStorage();
    $pipeline = $storage->create(['label' => 'Test pipeline']);
    $storage->save($pipeline);

    $loaded = $storage->load($pipeline->id());

    $this->assertSame($pipeline, $loaded);
  }

  /**
   * @covers ::load
   */
  public function testLoadReturnsNullForUnknownId(): void {
    $storage = $this->createStorage();

    $this->assertNull($storage->load('does-not-exist'));
  }

  // ---------------------------------------------------------------------------
  // delete()
  // ---------------------------------------------------------------------------

  /**
   * @covers ::delete
   */
  public function testDeleteRemovesPipelineFromStorage(): void {
    $storage = $this->createStorage();
    $pipeline = $storage->create([]);
    $storage->save($pipeline);
    $storage->delete($pipeline);

    $this->assertNull($storage->load($pipeline->id()));
  }

  // ---------------------------------------------------------------------------
  // resetCache()
  // ---------------------------------------------------------------------------

  /**
   * @covers ::resetCache
   */
  public function testResetCacheIsNoOpAndDoesNotRemovePipelines(): void {
    $storage = $this->createStorage();
    $pipeline = $storage->create([]);
    $storage->save($pipeline);

    // Must not throw and must not wipe data.
    $storage->resetCache([$pipeline->id()]);
    $this->assertSame($pipeline, $storage->load($pipeline->id()));

    $storage->resetCache();
    $this->assertSame($pipeline, $storage->load($pipeline->id()));
  }

  // ---------------------------------------------------------------------------
  // clear() / getAll()
  // ---------------------------------------------------------------------------

  /**
   * @covers \Drupal\flowdrop_runtime\Service\Storage\InMemoryPipelineStorage::clear
   * @covers \Drupal\flowdrop_runtime\Service\Storage\InMemoryPipelineStorage::getAll
   */
  public function testClearRemovesAllPipelines(): void {
    $uuid = $this->prophesize(UuidInterface::class);
    $uuid->generate()->willReturn(
      'aaaaaaaa-0000-0000-0000-000000000001',
      'aaaaaaaa-0000-0000-0000-000000000002',
    );
    $storage = new InMemoryPipelineStorage($uuid->reveal());

    $storage->save($storage->create([]));
    $storage->save($storage->create([]));
    $this->assertCount(2, $storage->getAll());

    $storage->clear();
    $this->assertCount(0, $storage->getAll());
  }

}
