<?php

declare(strict_types=1);

namespace Drupal\flowdrop_runtime\Service\Storage;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\flowdrop_job\FlowDropJobInterface;
use Drupal\flowdrop_pipeline\Entity\FlowDropPipelineInterface;
use Drupal\flowdrop_pipeline\Service\JobGenerationService;

/**
 * Drupal entity-based implementation of job storage.
 *
 * This implementation wraps Drupal's EntityTypeManager to provide
 * persistent storage for jobs using the flowdrop_job entity type.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class DrupalJobStorage implements JobStorageInterface {

  /**
   * The entity type manager.
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * The job generation service.
   */
  protected JobGenerationService $jobGenerationService;

  /**
   * Constructs a new DrupalJobStorage.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\flowdrop_pipeline\Service\JobGenerationService $jobGenerationService
   *   The job generation service.
   */
  public function __construct(EntityTypeManagerInterface $entityTypeManager, JobGenerationService $jobGenerationService) {
    $this->entityTypeManager = $entityTypeManager;
    $this->jobGenerationService = $jobGenerationService;
  }

  /**
   * {@inheritdoc}
   */
  public function create(array $data): FlowDropJobInterface {
    $storage = $this->entityTypeManager->getStorage('flowdrop_job');

    // Encode metadata if it's an array.
    if (isset($data['metadata']) && is_array($data['metadata'])) {
      $data['metadata'] = json_encode($data['metadata']);
    }

    // Handle pipeline_id -> pipeline reference.
    if (isset($data['pipeline_id'])) {
      $data['pipeline'] = ['target_id' => $data['pipeline_id']];
      unset($data['pipeline_id']);
    }

    // Handle dependent_jobs separately.
    $dependentJobs = $data['dependent_jobs'] ?? [];
    unset($data['dependent_jobs']);

    /** @var \Drupal\flowdrop_job\FlowDropJobInterface $job */
    $job = $storage->create($data);

    // Add dependent jobs.
    foreach ($dependentJobs as $dependentJobId) {
      $dependentJob = $this->load($dependentJobId);
      if ($dependentJob !== NULL) {
        $job->addDependentJob($dependentJob);
      }
    }

    return $job;
  }

  /**
   * {@inheritdoc}
   */
  public function load(string|int $id): ?FlowDropJobInterface {
    $storage = $this->entityTypeManager->getStorage('flowdrop_job');
    $job = $storage->load($id);

    return $job instanceof FlowDropJobInterface ? $job : NULL;
  }

  /**
   * {@inheritdoc}
   */
  public function loadMultiple(array $ids): array {
    $storage = $this->entityTypeManager->getStorage('flowdrop_job');
    $jobs = $storage->loadMultiple($ids);

    return array_filter($jobs, fn($job) => $job instanceof FlowDropJobInterface);
  }

  /**
   * {@inheritdoc}
   */
  public function save(FlowDropJobInterface $job): void {
    $job->save();
  }

  /**
   * {@inheritdoc}
   */
  public function delete(FlowDropJobInterface $job): void {
    $job->delete();
  }

  /**
   * {@inheritdoc}
   */
  public function getByPipeline(FlowDropPipelineInterface $pipeline): array {
    // Delegate to the pipeline's getJobs() method which handles the query.
    return $pipeline->getJobs();
  }

  /**
   * {@inheritdoc}
   */
  public function getByPipelineAndStatus(FlowDropPipelineInterface $pipeline, string $status): array {
    // Delegate to the pipeline's getJobsByStatus() method.
    return $pipeline->getJobsByStatus($status);
  }

  /**
   * {@inheritdoc}
   */
  public function getReadyJobs(FlowDropPipelineInterface $pipeline): array {
    return $this->jobGenerationService->getReadyJobs($pipeline);
  }

  /**
   * {@inheritdoc}
   */
  public function resetCache(?array $ids = NULL): void {
    $storage = $this->entityTypeManager->getStorage('flowdrop_job');
    $storage->resetCache($ids);
  }

}
