<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_pipeline\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\flowdrop_pipeline\Entity\FlowDropPipelineInterface;

/**
 * Covers pipeline lineage: postSave() self-root and parent/root stamping.
 *
 * @group flowdrop_pipeline
 */
class PipelineLineageTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'system',
    'user',
    'field',
    'options',
    'text',
    'flowdrop',
    'flowdrop_workflow',
    'flowdrop_orchestration',
    'flowdrop_runtime',
    'flowdrop_pipeline',
    'flowdrop_job',
    'flowdrop_node_type',
  ];

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

    $this->installEntitySchema('user');
    $this->installEntitySchema('flowdrop_workflow');
    $this->installEntitySchema('flowdrop_workflow_snapshot');
    $this->installEntitySchema('flowdrop_pipeline');
    $this->installEntitySchema('flowdrop_job');
    $this->installEntitySchema('flowdrop_node_type');
  }

  /**
   * A top-level pipeline self-roots to its own id on insert.
   */
  public function testTopLevelSelfRoots(): void {
    $pipeline = $this->createPipeline();

    // In-memory entity reflects the self-root without a reload.
    $this->assertSame((string) $pipeline->id(), $pipeline->getRootPipelineId());
    $this->assertNull($pipeline->getParentPipelineId());

    // And it is persisted, not just set in memory.
    $reloaded = $this->reload($pipeline);
    $this->assertSame((string) $pipeline->id(), $reloaded->getRootPipelineId());
    $this->assertNull($reloaded->getParentPipelineId());
  }

  /**
   * An inherited root (nested run) is preserved, not overwritten by self-root.
   */
  public function testInheritedLineagePreserved(): void {
    $root = $this->createPipeline();
    $sub = $this->createPipeline([
      'parent_pipeline_id' => $root->id(),
      'root_pipeline_id' => $root->id(),
    ]);

    $this->assertSame((string) $root->id(), $sub->getRootPipelineId());
    $this->assertSame((string) $root->id(), $sub->getParentPipelineId());
    $this->assertNotSame((string) $sub->id(), $sub->getRootPipelineId());

    $reloaded = $this->reload($sub);
    $this->assertSame((string) $root->id(), $reloaded->getRootPipelineId());
    $this->assertSame((string) $root->id(), $reloaded->getParentPipelineId());
  }

  /**
   * Self-root fires only on insert — a later update must not rewrite root.
   */
  public function testUpdateDoesNotRewriteRoot(): void {
    $root = $this->createPipeline();
    $sub = $this->createPipeline([
      'parent_pipeline_id' => $root->id(),
      'root_pipeline_id' => $root->id(),
    ]);

    // Re-save as an update (not an insert).
    $sub->setStatus('running')->save();

    $reloaded = $this->reload($sub);
    $this->assertSame((string) $root->id(), $reloaded->getRootPipelineId());
    $this->assertSame((string) $root->id(), $reloaded->getParentPipelineId());
  }

  /**
   * Creates and saves a pipeline with optional field overrides.
   *
   * @param array<string, mixed> $values
   *   Extra field values (e.g. parent_pipeline_id / root_pipeline_id).
   *
   * @return \Drupal\flowdrop_pipeline\Entity\FlowDropPipelineInterface
   *   The saved pipeline.
   */
  private function createPipeline(array $values = []): FlowDropPipelineInterface {
    $storage = $this->container->get('entity_type.manager')->getStorage('flowdrop_pipeline');
    $pipeline = $storage->create([
      'label' => 'Test Pipeline',
      'bundle' => 'default',
      'status' => 'pending',
    ] + $values);
    $pipeline->save();
    assert($pipeline instanceof FlowDropPipelineInterface);
    return $pipeline;
  }

  /**
   * Reloads a pipeline from storage, bypassing the static cache.
   */
  private function reload(FlowDropPipelineInterface $pipeline): FlowDropPipelineInterface {
    $storage = $this->container->get('entity_type.manager')->getStorage('flowdrop_pipeline');
    $storage->resetCache([$pipeline->id()]);
    $reloaded = $storage->load($pipeline->id());
    assert($reloaded instanceof FlowDropPipelineInterface);
    return $reloaded;
  }

}
