<?php

declare(strict_types=1);

namespace Drupal\flowdrop_interrupt\Service;

use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Session\AccountProxyInterface;
use Drupal\flowdrop_interrupt\DTO\InterruptCreationData;
use Drupal\flowdrop_interrupt\Entity\FlowDropInterruptInterface;
use Drupal\flowdrop_interrupt\Enum\InterruptDirection;
use Drupal\flowdrop_interrupt\Enum\InterruptStatus;
use Drupal\flowdrop_interrupt\Event\InterruptCreatedEvent;
use Psr\Log\LoggerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Writes an {@see InterruptCreationData} as a FlowDropInterrupt entity.
 *
 * Lives as a standalone service so {@see InterruptMessageHandlerInterface}
 * implementations can depend on it without dragging in the full manager
 * (which is what would otherwise create a construct-time DI cycle). The
 * manager composes this factory for its own `create()` method.
 *
 * @internal Plugins write via {@see InterruptMessageBusInterface} instead.
 */
final class InterruptFactory implements InterruptFactoryInterface {

  /**
   * The interrupt entity storage.
   */
  private EntityStorageInterface $storage;

  public function __construct(
    EntityTypeManagerInterface $entityTypeManager,
    private readonly AccountProxyInterface $currentUser,
    private readonly LoggerInterface $logger,
    private readonly EventDispatcherInterface $eventDispatcher,
  ) {
    $this->storage = $entityTypeManager->getStorage('flowdrop_interrupt');
  }

  /**
   * {@inheritdoc}
   */
  public function create(InterruptCreationData $data): FlowDropInterruptInterface {
    $uid = $data->assignedToUid ?? $this->currentUser->id();

    /** @var \Drupal\flowdrop_interrupt\Entity\FlowDropInterruptInterface $interrupt */
    $interrupt = $this->storage->create([
      'workflow_id' => $data->workflowId,
      'node_id' => $data->nodeId,
      'type' => $data->type->value,
      'status' => InterruptStatus::Pending->value,
      'message' => $data->message,
      'uid' => $uid,
    ]);

    $interrupt->setSchema($data->schema);
    $interrupt->setOptions($data->options);
    $interrupt->setContext($data->context);
    $interrupt->setDefaultValue($data->defaultValue);

    if ($data->pipelineId !== NULL) {
      $interrupt->setPipelineId($data->pipelineId);
    }

    if ($data->sessionId !== NULL) {
      $interrupt->setSessionId($data->sessionId);
    }

    if ($data->expiresIn !== NULL && $data->expiresIn > 0) {
      $interrupt->setExpires(time() + $data->expiresIn);
    }

    if ($data->scheduledAt !== NULL && $data->scheduledAt > 0) {
      $interrupt->setScheduledAt($data->scheduledAt);
    }

    // Direction defaults to Outward when the message handler doesn't stamp it.
    $interrupt->setDirection($data->direction ?? InterruptDirection::Outward);

    if ($data->jobId !== NULL) {
      $interrupt->setJobId($data->jobId);
    }

    if ($data->linkedInterruptId !== NULL && $data->linkedInterruptId !== '') {
      $interrupt->setLinkedInterruptId($data->linkedInterruptId);
    }

    if ($data->targetPipelineId !== NULL) {
      $interrupt->setTargetPipelineId($data->targetPipelineId);
    }

    if ($data->initiatorUid !== NULL) {
      $interrupt->setInitiatorUid($data->initiatorUid);
    }

    if ($data->reason !== NULL) {
      $interrupt->setReason($data->reason);
    }

    $interrupt->save();

    $this->logger->info('Created @type interrupt @id for workflow @workflow, node @node', [
      '@type' => $data->type->value,
      '@id' => $interrupt->uuid(),
      '@workflow' => $data->workflowId,
      '@node' => $data->nodeId,
    ]);

    $this->eventDispatcher->dispatch(
      new InterruptCreatedEvent(
        interrupt: $interrupt,
        type: $data->type->value,
        workflowId: $data->workflowId,
        nodeId: $data->nodeId,
        sessionId: $data->sessionId,
        pipelineId: $data->pipelineId,
      ),
      InterruptCreatedEvent::NAME,
    );

    return $interrupt;
  }

}
