<?php

declare(strict_types=1);

namespace Drupal\flowdrop_runtime\Service\Orchestrator\Trait;

use Drupal\flowdrop_job\Constants\JobStatus;
use Drupal\flowdrop_pipeline\Entity\FlowDropPipelineRuntimeInterface;
use Drupal\flowdrop_runtime\Service\Storage\JobStorageInterface;
use Psr\Log\LoggerInterface;

/**
 * Provides common job skipping logic for orchestrators.
 *
 * This trait marks remaining idle and pending jobs as skipped when a workflow
 * completes or fails. This is necessary for conditional workflows where only
 * one branch is taken (e.g., in an if-else workflow, the untaken branch's
 * jobs should be marked as skipped).
 *
 * Requirements for using this trait:
 * - The class must implement getJobSkippingLogger() returning LoggerInterface
 * - The class must implement getJobSkippingStorage() returning
 *   JobStorageInterface
 *
 * @see \Drupal\flowdrop_runtime\Service\Orchestrator\InterruptHandlingTrait
 *
 * @internal This trait is not part of the public API and may change without notice.
 */
trait JobSkippingTrait {

  /**
   * Marks all remaining idle and pending jobs as skipped.
   *
   * Called when a workflow completes or fails to mark jobs that were
   * never executed due to conditional branching or early termination.
   *
   * @param \Drupal\flowdrop_pipeline\Entity\FlowDropPipelineRuntimeInterface $pipeline
   *   The pipeline containing the jobs.
   */
  protected function markRemainingJobsAsSkipped(
    FlowDropPipelineRuntimeInterface $pipeline,
  ): void {
    $skippedCount = 0;
    $jobStorage = $this->getJobSkippingStorage();

    // Mark all idle jobs as skipped.
    $idleJobs = $pipeline->getJobsByStatus(JobStatus::IDLE);
    foreach ($idleJobs as $job) {
      $job->markAsSkipped();
      $jobStorage->save($job);
      $skippedCount++;
    }

    // Mark all pending jobs as skipped.
    // These are jobs that became ready but were never executed because
    // the workflow completed before they could run.
    $pendingJobs = $pipeline->getJobsByStatus(JobStatus::PENDING);
    foreach ($pendingJobs as $job) {
      $job->markAsSkipped();
      $jobStorage->save($job);
      $skippedCount++;
    }

    if ($skippedCount > 0) {
      $this->getJobSkippingLogger()->info("Marked @count remaining jobs as skipped", [
        "@count" => $skippedCount,
      ]);
    }
  }

  /**
   * Gets the logger for job skipping operations.
   *
   * @return \Psr\Log\LoggerInterface
   *   The logger service.
   */
  abstract protected function getJobSkippingLogger(): LoggerInterface;

  /**
   * Gets the job storage service for job skipping operations.
   *
   * @return \Drupal\flowdrop_runtime\Service\Storage\JobStorageInterface
   *   The job storage service.
   */
  abstract protected function getJobSkippingStorage(): JobStorageInterface;

}
