<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_runtime\Unit\Service;

use Drupal\flowdrop_runtime\Service\ParameterResolver;
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\LoggerInterface;

/**
 * Tests that ParameterResolver gates runtime/LLM override on port exposure.
 *
 * Phase B semantic exposure: a not-exposed connectable input port is not
 * runtime-overridable, so a runtime/LLM value for it is ignored and the
 * author's config value (Priority 2) wins. Exposure is the instance's
 * `config.ports` override (in workflowValues) else the param's metadata
 * `x-exposed-by-default`.
 *
 * @coversDefaultClass \Drupal\flowdrop_runtime\Service\ParameterResolver
 * @group flowdrop_runtime
 */
class ParameterResolverExposureTest extends TestCase {

  use ProphecyTrait;

  /**
   * The service under test.
   */
  protected ParameterResolver $resolver;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $logger = $this->prophesize(LoggerInterface::class);
    $this->resolver = new ParameterResolver($logger->reveal());
  }

  /**
   * Resolves the single "value" param given a ports override + schema default.
   *
   * @param array<string, mixed> $ports
   *   The config.ports value (empty for none).
   * @param bool $exposedByDefault
   *   The param's metadata default exposure.
   *
   * @return mixed
   *   The resolved value of "value".
   */
  private function resolveValueParam(array $ports, bool $exposedByDefault = TRUE): mixed {
    $schema = ['type' => 'object', 'properties' => ['value' => ['type' => 'string']]];
    if (!$exposedByDefault) {
      $schema['properties']['value']['x-exposed-by-default'] = FALSE;
    }
    $entityConfig = ['value' => ['connectable' => TRUE, 'configurable' => TRUE]];
    $workflowValues = ['value' => 'from-config'];
    if ($ports !== []) {
      $workflowValues['ports'] = $ports;
    }
    $runtimeInputs = ['value' => 'from-runtime'];

    return $this->resolver
      ->resolve($schema, $entityConfig, $workflowValues, $runtimeInputs)
      ->get('value');
  }

  /**
   * An exposed connectable param takes the runtime/LLM value (Priority 1).
   *
   * @covers ::resolve
   */
  public function testExposedParamTakesRuntimeInput(): void {
    $this->assertSame('from-runtime', $this->resolveValueParam([]));
  }

  /**
   * A param hidden via config.ports ignores the runtime value (author wins).
   *
   * @covers ::resolve
   */
  public function testNotExposedParamIgnoresRuntimeInput(): void {
    $ports = ['inputs' => [['id' => 'value', 'exposed' => FALSE]]];
    $this->assertSame('from-config', $this->resolveValueParam($ports));
  }

  /**
   * A param shipping `x-exposed-by-default: false` ignores the runtime value.
   *
   * @covers ::resolve
   */
  public function testDefaultNotExposedParamIgnoresRuntimeInput(): void {
    $this->assertSame('from-config', $this->resolveValueParam([], FALSE));
  }

  /**
   * Re-exposing a default-not-exposed param restores runtime override.
   *
   * @covers ::resolve
   */
  public function testReExposedParamTakesRuntimeInput(): void {
    $ports = ['inputs' => [['id' => 'value', 'exposed' => TRUE]]];
    $this->assertSame('from-runtime', $this->resolveValueParam($ports, FALSE));
  }

}
