<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_trigger\Unit\EventSubscriber;

use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\flowdrop_trigger\Entity\FlowDropTriggerConfig;
use Drupal\flowdrop_trigger\EventSubscriber\WorkflowDuplicateSubscriber;
use Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface;
use Drupal\flowdrop_trigger\Service\TriggerManagerInterface;
use Drupal\flowdrop_workflow\Event\WorkflowDuplicatedEvent;
use Drupal\flowdrop_workflow\FlowDropWorkflowInterface;
use Drupal\Tests\UnitTestCase;
use Prophecy\Argument;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * Tests the WorkflowDuplicateSubscriber class.
 *
 * @coversDefaultClass \Drupal\flowdrop_trigger\EventSubscriber\WorkflowDuplicateSubscriber
 * @group flowdrop_trigger
 */
class WorkflowDuplicateSubscriberTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * The entity type manager mock.
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|\Drupal\Core\Entity\EntityTypeManagerInterface
   */
  private $entityTypeManager;

  /**
   * The trigger config storage mock.
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|\Drupal\Core\Entity\EntityStorageInterface
   */
  private $storage;

  /**
   * The trigger manager mock.
   *
   * @var \Prophecy\Prophecy\ObjectProphecy|\Drupal\flowdrop_trigger\Service\TriggerManagerInterface
   */
  private $triggerManager;

  /**
   * The subscriber under test.
   *
   * @var \Drupal\flowdrop_trigger\EventSubscriber\WorkflowDuplicateSubscriber
   */
  private WorkflowDuplicateSubscriber $subscriber;

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

    $this->storage = $this->prophesize(EntityStorageInterface::class);
    $this->entityTypeManager = $this->prophesize(EntityTypeManagerInterface::class);
    $this->entityTypeManager->getStorage('flowdrop_trigger_config')
      ->willReturn($this->storage->reveal());
    $this->triggerManager = $this->prophesize(TriggerManagerInterface::class);

    $this->subscriber = new WorkflowDuplicateSubscriber(
      $this->entityTypeManager->reveal(),
      $this->triggerManager->reveal(),
    );
  }

  /**
   * Builds an event for a duplicate whose new workflow has the given ID.
   */
  private function event(string $sourceId, string $newId): WorkflowDuplicatedEvent {
    $newWorkflow = $this->prophesize(FlowDropWorkflowInterface::class);
    $newWorkflow->id()->willReturn($newId);
    return new WorkflowDuplicatedEvent($sourceId, $newWorkflow->reveal());
  }

  /**
   * @covers ::onWorkflowDuplicated
   */
  public function testClonesConfigToNewWorkflowId(): void {
    $source = $this->prophesize(FlowDropTriggerConfigInterface::class);
    $source->getNodeId()->willReturn('trg');
    $source->label()->willReturn('Trigger: user.login');
    $source->getDescription()->willReturn('desc');
    $source->getEventType()->willReturn('user.login');
    $source->getConditions()->willReturn(['roles' => ['editor']]);
    $source->getOrchestratorSettings()->willReturn(['type' => 'async']);
    $source->getInitialDataMapping()->willReturn([]);
    $source->status()->willReturn(TRUE);
    $source->getWeight()->willReturn(3);

    $this->triggerManager->getTriggersForWorkflow('src')
      ->willReturn([$source->reveal()]);

    // No existing config with the derived ID → clone is created.
    $newId = FlowDropTriggerConfig::generateId('copy', 'trg');
    $this->storage->load($newId)->willReturn(NULL);

    $created = $this->prophesize(FlowDropTriggerConfigInterface::class);
    $created->save()->shouldBeCalledOnce();

    $this->storage->create(Argument::that(function (array $values) use ($newId): bool {
      return $values['id'] === $newId
        && $values['workflow_id'] === 'copy'
        && $values['node_id'] === 'trg'
        && $values['event_type'] === 'user.login'
        && $values['conditions'] === ['roles' => ['editor']]
        && $values['orchestrator_settings'] === ['type' => 'async']
        && $values['weight'] === 3
        && $values['status'] === TRUE;
    }))->willReturn($created->reveal())->shouldBeCalledOnce();

    $this->subscriber->onWorkflowDuplicated($this->event('src', 'copy'));
  }

  /**
   * @covers ::onWorkflowDuplicated
   */
  public function testSkipsWhenTargetConfigAlreadyExists(): void {
    $source = $this->prophesize(FlowDropTriggerConfigInterface::class);
    $source->getNodeId()->willReturn('trg');

    $this->triggerManager->getTriggersForWorkflow('src')
      ->willReturn([$source->reveal()]);

    $existing = $this->prophesize(FlowDropTriggerConfigInterface::class);
    $this->storage->load(FlowDropTriggerConfig::generateId('copy', 'trg'))
      ->willReturn($existing->reveal());

    $this->storage->create(Argument::cetera())->shouldNotBeCalled();

    $this->subscriber->onWorkflowDuplicated($this->event('src', 'copy'));
  }

  /**
   * @covers ::onWorkflowDuplicated
   */
  public function testNoSourceConfigsIsNoOp(): void {
    $this->triggerManager->getTriggersForWorkflow('src')->willReturn([]);

    $this->storage->load(Argument::cetera())->shouldNotBeCalled();
    $this->storage->create(Argument::cetera())->shouldNotBeCalled();

    $this->subscriber->onWorkflowDuplicated($this->event('src', 'copy'));
  }

}
