<?php

declare(strict_types=1);

namespace Drupal\flowdrop_interrupt\Entity;

use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\Attribute\ContentEntityType;
use Drupal\Core\Entity\ContentEntityBase;
use Drupal\Core\Entity\EntityChangedTrait;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop_interrupt\Access\FlowDropInterruptAccessControlHandler;
use Drupal\flowdrop_interrupt\Enum\InterruptDirection;
use Drupal\flowdrop_interrupt\Enum\InterruptStatus;
use Drupal\flowdrop_interrupt\Enum\InterruptType;
use Drupal\user\EntityOwnerTrait;
use Drupal\views\EntityViewsData;

/**
 * Defines the FlowDrop Interrupt entity.
 *
 * Stores interrupt requests for human-in-loop workflows. When a workflow
 * encounters an interrupt node, it creates an interrupt entity and pauses
 * execution until the user provides a response.
 */
#[ContentEntityType(
  id: 'flowdrop_interrupt',
  label: new TranslatableMarkup('FlowDrop Interrupt'),
  label_collection: new TranslatableMarkup('FlowDrop Interrupts'),
  label_singular: new TranslatableMarkup('interrupt'),
  label_plural: new TranslatableMarkup('interrupts'),
  entity_keys: [
    'id' => 'id',
    'uuid' => 'uuid',
    'owner' => 'uid',
  ],
  handlers: [
    'storage' => 'Drupal\Core\Entity\Sql\SqlContentEntityStorage',
    'access' => FlowDropInterruptAccessControlHandler::class,
    'views_data' => EntityViewsData::class,
  ],
  base_table: 'flowdrop_interrupt',
  admin_permission: 'administer flowdrop interrupts',
  label_count: [
    'singular' => '@count interrupt',
    'plural' => '@count interrupts',
  ],
)]
class FlowDropInterrupt extends ContentEntityBase implements FlowDropInterruptInterface {

  use EntityChangedTrait;
  use EntityOwnerTrait;

  /**
   * {@inheritdoc}
   */
  public function getWorkflowId(): string {
    $value = $this->get("workflow_id")->value;
    return $value !== NULL ? (string) $value : "";
  }

  /**
   * {@inheritdoc}
   */
  public function setWorkflowId(string $workflow_id): static {
    $this->set("workflow_id", $workflow_id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getPipelineId(): ?string {
    $value = $this->get("pipeline_id")->value;
    return $value !== NULL && $value !== "" ? (string) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setPipelineId(?string $pipeline_id): static {
    $this->set("pipeline_id", $pipeline_id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getSessionId(): ?int {
    $value = $this->get("session_id")->target_id;
    return $value !== NULL ? (int) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setSessionId(?int $session_id): static {
    if ($session_id === NULL) {
      $this->set("session_id", NULL);
    }
    else {
      $this->set("session_id", ["target_id" => $session_id]);
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getNodeId(): string {
    $value = $this->get("node_id")->value;
    return $value !== NULL ? (string) $value : "";
  }

  /**
   * {@inheritdoc}
   */
  public function setNodeId(string $node_id): static {
    $this->set("node_id", $node_id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getType(): InterruptType {
    $value = $this->get("type")->value;
    return InterruptType::tryFrom((string) $value) ?? InterruptType::Confirmation;
  }

  /**
   * {@inheritdoc}
   */
  public function setType(InterruptType $type): static {
    $this->set("type", $type->value);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getStatus(): InterruptStatus {
    $value = $this->get("status")->value;
    return InterruptStatus::tryFrom((string) $value) ?? InterruptStatus::Pending;
  }

  /**
   * {@inheritdoc}
   */
  public function setStatus(InterruptStatus $status): static {
    $this->set("status", $status->value);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getMessage(): string {
    $value = $this->get("message")->value;
    return $value !== NULL ? (string) $value : "";
  }

  /**
   * {@inheritdoc}
   */
  public function setMessage(string $message): static {
    $this->set("message", $message);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getSchema(): array {
    $data = $this->get("schema")->value;
    if ($data === NULL || $data === "") {
      return [];
    }
    $decoded = Json::decode($data);
    return is_array($decoded) ? $decoded : [];
  }

  /**
   * {@inheritdoc}
   */
  public function setSchema(array $schema): static {
    $this->set("schema", Json::encode($schema));
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getOptions(): array {
    $data = $this->get("options")->value;
    if ($data === NULL || $data === "") {
      return [];
    }
    $decoded = Json::decode($data);
    return is_array($decoded) ? $decoded : [];
  }

  /**
   * {@inheritdoc}
   */
  public function setOptions(array $options): static {
    $this->set("options", Json::encode($options));
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getDefaultValue(): mixed {
    $data = $this->get("default_value")->value;
    if ($data === NULL || $data === "") {
      return NULL;
    }
    return Json::decode($data);
  }

  /**
   * {@inheritdoc}
   */
  public function setDefaultValue(mixed $value): static {
    $this->set("default_value", Json::encode($value));
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getResponseData(): mixed {
    $data = $this->get("response_data")->value;
    if ($data === NULL || $data === "") {
      return NULL;
    }
    return Json::decode($data);
  }

  /**
   * {@inheritdoc}
   */
  public function setResponseData(mixed $data): static {
    $this->set("response_data", Json::encode($data));
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getContext(): array {
    $data = $this->get("context")->value;
    if ($data === NULL || $data === "") {
      return [];
    }
    $decoded = Json::decode($data);
    return is_array($decoded) ? $decoded : [];
  }

  /**
   * {@inheritdoc}
   */
  public function setContext(array $context): static {
    $this->set("context", Json::encode($context));
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getExpires(): ?int {
    $value = $this->get("expires")->value;
    return $value !== NULL ? (int) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setExpires(?int $timestamp): static {
    $this->set("expires", $timestamp);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getScheduledAt(): ?int {
    $value = $this->get("scheduled_at")->value;
    return $value !== NULL && (int) $value > 0 ? (int) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setScheduledAt(?int $timestamp): static {
    $this->set("scheduled_at", $timestamp ?? 0);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getResolvedBy(): ?int {
    $value = $this->get("resolved_by")->target_id;
    return $value !== NULL ? (int) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setResolvedBy(?int $user_id): static {
    if ($user_id === NULL) {
      $this->set("resolved_by", NULL);
    }
    else {
      $this->set("resolved_by", ["target_id" => $user_id]);
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getResolvedAt(): ?int {
    $value = $this->get("resolved_at")->value;
    return $value !== NULL ? (int) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setResolvedAt(?int $timestamp): static {
    $this->set("resolved_at", $timestamp);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getCreatedTime(): int {
    $value = $this->get("created")->value;
    return $value !== NULL ? (int) $value : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function getChangedTime(): int {
    $value = $this->get("changed")->value;
    return $value !== NULL ? (int) $value : 0;
  }

  /**
   * {@inheritdoc}
   */
  public function isPending(): bool {
    return $this->getStatus() === InterruptStatus::Pending;
  }

  /**
   * {@inheritdoc}
   */
  public function isResolved(): bool {
    return $this->getStatus() === InterruptStatus::Resolved;
  }

  /**
   * {@inheritdoc}
   */
  public function isExpired(): bool {
    $status = $this->getStatus();
    if ($status === InterruptStatus::Expired) {
      return TRUE;
    }

    // Check if past expiration time.
    $expires = $this->getExpires();
    if ($expires !== NULL && $expires > 0 && time() > $expires) {
      return TRUE;
    }

    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function resolve(mixed $response_data, ?int $user_id = NULL): static {
    $this->setStatus(InterruptStatus::Resolved);
    $this->setResponseData($response_data);
    $this->setResolvedBy($user_id);
    $this->setResolvedAt(time());
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function cancel(): static {
    $this->setStatus(InterruptStatus::Cancelled);
    $this->setResolvedAt(time());
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function expire(): static {
    $this->setStatus(InterruptStatus::Expired);
    $this->setResolvedAt(time());
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getInterruptId(): string {
    return (string) ($this->uuid() ?? '');
  }

  /**
   * {@inheritdoc}
   */
  public function getDirection(): InterruptDirection {
    $value = $this->get("direction")->value;
    return InterruptDirection::tryFrom((string) $value) ?? InterruptDirection::Outward;
  }

  /**
   * {@inheritdoc}
   */
  public function setDirection(InterruptDirection $direction): static {
    $this->set("direction", $direction->value);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getJobId(): ?int {
    $value = $this->get("job_id")->target_id;
    return $value !== NULL ? (int) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setJobId(?int $job_id): static {
    if ($job_id === NULL) {
      $this->set("job_id", NULL);
    }
    else {
      $this->set("job_id", ["target_id" => $job_id]);
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getTargetPipelineId(): ?string {
    $value = $this->get("target_pipeline_id")->value;
    return $value !== NULL && $value !== "" ? (string) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setTargetPipelineId(?string $pipeline_id): static {
    $this->set("target_pipeline_id", $pipeline_id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getInitiatorUid(): ?int {
    $value = $this->get("initiator_uid")->target_id;
    return $value !== NULL ? (int) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setInitiatorUid(?int $uid): static {
    if ($uid === NULL) {
      $this->set("initiator_uid", NULL);
    }
    else {
      $this->set("initiator_uid", ["target_id" => $uid]);
    }
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getReason(): ?string {
    $value = $this->get("reason")->value;
    return $value !== NULL && $value !== "" ? (string) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setReason(?string $reason): static {
    $this->set("reason", $reason);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getLinkedInterruptId(): ?string {
    $value = $this->get("linked_interrupt_id")->value;
    return $value !== NULL && $value !== "" ? (string) $value : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function setLinkedInterruptId(?string $linked_interrupt_id): static {
    $this->set("linked_interrupt_id", $linked_interrupt_id);
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function getEffectivePipelineId(): ?string {
    return $this->getDirection() === InterruptDirection::Inward
      ? $this->getTargetPipelineId()
      : $this->getPipelineId();
  }

  /**
   * {@inheritdoc}
   */
  public function acknowledge(): static {
    $this->setStatus(InterruptStatus::Acknowledged);
    $this->setResolvedAt(time());
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public function toApiArray(): array {
    return [
      "id" => $this->uuid(),
      "type" => $this->getType()->value,
      "status" => $this->getStatus()->value,
      "message" => $this->getMessage(),
      "nodeId" => $this->getNodeId(),
      "workflowId" => $this->getWorkflowId(),
      "pipelineId" => $this->getPipelineId(),
      "sessionId" => $this->getSessionId(),
      "schema" => $this->getSchema() ?: NULL,
      "options" => $this->getOptions() ?: NULL,
      "context" => $this->getContext() ?: NULL,
      "defaultValue" => $this->getDefaultValue(),
      "responseData" => $this->getResponseData(),
      "createdAt" => date("c", $this->getCreatedTime()),
      "expiresAt" => $this->getExpires() !== NULL ? date("c", $this->getExpires()) : NULL,
      "scheduledAt" => $this->getScheduledAt() !== NULL ? date("c", $this->getScheduledAt()) : NULL,
      "resolvedAt" => $this->getResolvedAt() !== NULL ? date("c", $this->getResolvedAt()) : NULL,
      "resolvedBy" => $this->getResolvedBy(),
      "direction" => $this->getDirection()->value,
      "jobId" => $this->getJobId(),
      "targetPipelineId" => $this->getTargetPipelineId(),
      "initiatorUid" => $this->getInitiatorUid(),
      "reason" => $this->getReason(),
      "linkedInterruptId" => $this->getLinkedInterruptId(),
    ];
  }

  /**
   * {@inheritdoc}
   */
  public static function baseFieldDefinitions(EntityTypeInterface $entity_type): array {
    $fields = parent::baseFieldDefinitions($entity_type);

    // Add the owner field.
    $fields += static::ownerBaseFieldDefinitions($entity_type);

    $fields["workflow_id"] = BaseFieldDefinition::create("string")
      ->setLabel(new TranslatableMarkup("Workflow ID"))
      ->setDescription(new TranslatableMarkup("The workflow that triggered this interrupt."))
      ->setRequired(TRUE)
      ->setSettings([
        "max_length" => 255,
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["pipeline_id"] = BaseFieldDefinition::create("string")
      ->setLabel(new TranslatableMarkup("Pipeline ID"))
      ->setDescription(new TranslatableMarkup("The pipeline/execution ID (optional)."))
      ->setRequired(FALSE)
      ->setSettings([
        "max_length" => 255,
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["session_id"] = BaseFieldDefinition::create("entity_reference")
      ->setLabel(new TranslatableMarkup("Session"))
      ->setDescription(new TranslatableMarkup("The playground session (optional)."))
      ->setRequired(FALSE)
      ->setSetting("target_type", "flowdrop_session")
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["node_id"] = BaseFieldDefinition::create("string")
      ->setLabel(new TranslatableMarkup("Node ID"))
      ->setDescription(new TranslatableMarkup("The node that triggered this interrupt."))
      ->setRequired(TRUE)
      ->setSettings([
        "max_length" => 255,
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["type"] = BaseFieldDefinition::create("string")
      ->setLabel(new TranslatableMarkup("Type"))
      ->setDescription(new TranslatableMarkup("The interrupt type (confirmation, choice, text, form)."))
      ->setRequired(TRUE)
      ->setDefaultValue("confirmation")
      ->setSettings([
        "max_length" => 32,
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["status"] = BaseFieldDefinition::create("string")
      ->setLabel(new TranslatableMarkup("Status"))
      ->setDescription(new TranslatableMarkup("The interrupt status (pending, resolved, expired, cancelled)."))
      ->setRequired(TRUE)
      ->setDefaultValue(InterruptStatus::Pending->value)
      ->setSettings([
        "max_length" => 32,
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["message"] = BaseFieldDefinition::create("string_long")
      ->setLabel(new TranslatableMarkup("Message"))
      ->setDescription(new TranslatableMarkup("The message to display to the user."))
      ->setRequired(TRUE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["schema"] = BaseFieldDefinition::create("string_long")
      ->setLabel(new TranslatableMarkup("Schema"))
      ->setDescription(new TranslatableMarkup("JSON Schema for input validation."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", FALSE);

    $fields["options"] = BaseFieldDefinition::create("string_long")
      ->setLabel(new TranslatableMarkup("Options"))
      ->setDescription(new TranslatableMarkup("Options for choice-type interrupts (JSON)."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", FALSE);

    $fields["default_value"] = BaseFieldDefinition::create("string_long")
      ->setLabel(new TranslatableMarkup("Default Value"))
      ->setDescription(new TranslatableMarkup("Default/pre-filled value (JSON)."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", FALSE);

    $fields["response_data"] = BaseFieldDefinition::create("string_long")
      ->setLabel(new TranslatableMarkup("Response Data"))
      ->setDescription(new TranslatableMarkup("User's response data (JSON)."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", FALSE);

    $fields["context"] = BaseFieldDefinition::create("string_long")
      ->setLabel(new TranslatableMarkup("Context"))
      ->setDescription(new TranslatableMarkup("Execution context for resuming (JSON)."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", FALSE);

    $fields["expires"] = BaseFieldDefinition::create("timestamp")
      ->setLabel(new TranslatableMarkup("Expires"))
      ->setDescription(new TranslatableMarkup("When the interrupt expires."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["scheduled_at"] = BaseFieldDefinition::create("timestamp")
      ->setLabel(new TranslatableMarkup("Scheduled at"))
      ->setDescription(new TranslatableMarkup("When a scheduled interrupt becomes due for automatic resolution. Distinct from expires: a due timer is resolved, an expired interrupt is discarded."))
      ->setRequired(FALSE)
      ->setDefaultValue(0)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["resolved_by"] = BaseFieldDefinition::create("entity_reference")
      ->setLabel(new TranslatableMarkup("Resolved By"))
      ->setDescription(new TranslatableMarkup("The user who resolved the interrupt."))
      ->setRequired(FALSE)
      ->setSetting("target_type", "user")
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["resolved_at"] = BaseFieldDefinition::create("timestamp")
      ->setLabel(new TranslatableMarkup("Resolved At"))
      ->setDescription(new TranslatableMarkup("When the interrupt was resolved."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    $fields["created"] = BaseFieldDefinition::create("created")
      ->setLabel(new TranslatableMarkup("Created"))
      ->setDescription(new TranslatableMarkup("The time that the interrupt was created."));

    $fields["changed"] = BaseFieldDefinition::create("changed")
      ->setLabel(new TranslatableMarkup("Changed"))
      ->setDescription(new TranslatableMarkup("The time that the interrupt was last updated."));

    // Direction: outward (raised by a node, awaits external resolution) or
    // inward (external signal targeting a pipeline — Cancel / Pause).
    // The composite (direction, status, target_pipeline_id) index needed
    // for Phase 3's inward signal poll lands with Phase 3 — for now the
    // column is unindexed since no queries filter by direction yet.
    $fields["direction"] = BaseFieldDefinition::create("list_string")
      ->setLabel(new TranslatableMarkup("Direction"))
      ->setDescription(new TranslatableMarkup("Outward (from a node) or inward (targeting a pipeline)."))
      ->setRequired(TRUE)
      ->setDefaultValue(InterruptDirection::Outward->value)
      ->setSetting("allowed_values", [
        InterruptDirection::Outward->value => 'Outward',
        InterruptDirection::Inward->value => 'Inward',
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    // Job reference: the job entity that's interrupted by this row. Set by
    // the orchestrator on catch — enables O(1) job lookup on resume instead
    // of scanning the pipeline's job list. NULL for inward signals and
    // legacy rows that haven't been backfilled.
    //
    // Drupal SQL storage indexes `entity_reference.target_id` automatically
    // via SqlContentEntityStorageSchema, so resume's "find job by interrupt"
    // path is O(1) without a setIndexes() call.
    $fields["job_id"] = BaseFieldDefinition::create("entity_reference")
      ->setLabel(new TranslatableMarkup("Interrupted Job"))
      ->setDescription(new TranslatableMarkup("The job that's interrupted by this row."))
      ->setRequired(FALSE)
      ->setSetting("target_type", "flowdrop_job")
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    // Target pipeline ID: the pipeline an inward signal acts ON. NULL for
    // outward interrupts. Phase 3 inward-signal poll uses this with
    // (direction, status) for indexed lookup.
    $fields["target_pipeline_id"] = BaseFieldDefinition::create("string")
      ->setLabel(new TranslatableMarkup("Target Pipeline ID"))
      ->setDescription(new TranslatableMarkup("Pipeline the signal acts on (inward signals only)."))
      ->setRequired(FALSE)
      ->setSettings([
        "max_length" => 255,
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    // Initiator UID: audit trail for inward signals — who raised the
    // Cancel/Pause. NULL for outward interrupts.
    $fields["initiator_uid"] = BaseFieldDefinition::create("entity_reference")
      ->setLabel(new TranslatableMarkup("Initiator"))
      ->setDescription(new TranslatableMarkup("User who initiated this signal (inward signals only)."))
      ->setRequired(FALSE)
      ->setSetting("target_type", "user")
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    // Reason: free-form text the initiator provided. Surfaced in the
    // inbox for paused pipelines.
    $fields["reason"] = BaseFieldDefinition::create("string_long")
      ->setLabel(new TranslatableMarkup("Reason"))
      ->setDescription(new TranslatableMarkup("Free-form reason for raising this signal."))
      ->setRequired(FALSE)
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    // Linked interrupt UUID: when this interrupt is gated on another
    // interrupt's resolution, the cascade subscriber resolves this one
    // with the same response_data when the linked interrupt resolves.
    // Stored as the linked entity's UUID (string) for symmetry with the
    // rest of the interrupt module's external API. The composite index
    // (linked_interrupt_id, status) lets the cascade subscriber query
    // pending followers in O(1) on resolution.
    $fields["linked_interrupt_id"] = BaseFieldDefinition::create("string")
      ->setLabel(new TranslatableMarkup("Linked Interrupt"))
      ->setDescription(new TranslatableMarkup("UUID of the interrupt whose resolution unblocks this one."))
      ->setRequired(FALSE)
      ->setSettings([
        "max_length" => 128,
      ])
      ->setDisplayConfigurable("form", FALSE)
      ->setDisplayConfigurable("view", TRUE);

    return $fields;
  }

}
