<?php

declare(strict_types=1);

namespace Drupal\flowdrop_ai_context\Plugin\FlowDropNodeProcessor;

use Drupal\ai_context\Model\AiContextRequest;
use Drupal\ai_context\Service\AiContextRenderer;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop\Attribute\FlowDropNodeProcessor;
use Drupal\flowdrop\DTO\ParameterBagInterface;
use Drupal\flowdrop\DTO\ValidationResult;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Injects a single AI Context Item's content into a FlowDrop workflow.
 *
 * The primary output is rendered_text — a structured block produced by
 * AiContextRenderer (id/label/purpose/guidance) that AI agents understand
 * natively. Connect it to the additional_context input on AI Agent Executor.
 */
#[FlowDropNodeProcessor(
  id: 'ai_context_injector',
  label: new TranslatableMarkup('AI Context Item'),
  description: 'Inject a specific, manually selected AI Context Item into the workflow',
  version: '1.0.0',
)]
final class AiContextInjector extends AiContextProcessorBase {

  /**
   * Constructs an AiContextInjector processor.
   */
  public function __construct(
    array $configuration,
    string $plugin_id,
    array $plugin_definition,
    protected readonly EntityTypeManagerInterface $entityTypeManager,
    protected readonly AiContextRenderer $renderer,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    $instance = new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager'),
      $container->get('ai_context.renderer'),
    );
    $instance->setUsageTracker($container->get('ai_context.usage_tracker'));
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function getParameterSchema(): array {
    return [
      'type' => 'object',
      'properties' => [
        'context_id' => [
          'type' => 'string',
          'title' => 'AI Context Item',
          'description' => 'Select the AI Context Item to inject into the workflow',
          'default' => '',
          'required' => TRUE,
          // Selects the flowdrop_ai_context field widget, which lists the
          // site's context items with their labels and a preview. Without a
          // format the editor falls back to a plain <select> whose option text
          // is the raw entity ID.
          //
          // Deliberately no 'enum' here. Deriving one from the existing context
          // items looked like useful validation but made the node unsavable in
          // three ways: the declared default ('') was not a member of its own
          // enum, so a node with nothing selected yet failed R6_CONFIG_INVALID;
          // the list came from an access-checked entity query, so the allowed
          // set varied per user and a workflow one editor saved another could
          // not; and it ran that query on every schema build, including the
          // node-list API that builds every node type's schema. Presence and
          // existence are enforced where they belong — validateParams() rejects
          // an empty ID and process() fails on one that does not resolve.
          'format' => 'ai_context_item_selector',
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function getOutputSchema(): array {
    return [
      'type' => 'object',
      'properties' => [
        'rendered_text' => [
          'type' => 'string',
          'description' => 'AI-ready structured block (id/label/purpose/guidance). Connect to additional_context on AI Agent Executor.',
        ],
        'content' => [
          'type' => 'string',
          'description' => 'Raw markdown content of the context item',
        ],
        'purpose' => [
          'type' => 'string',
          'description' => 'The purpose field — when AI should use this context',
        ],
        'label' => [
          'type' => 'string',
          'description' => 'Admin label of the context item',
        ],
        'context_id' => [
          'type' => 'string',
          'description' => 'ID of the loaded context item',
        ],
        'success' => [
          'type' => 'boolean',
          'description' => 'Whether the context item was loaded successfully',
        ],
        'error_message' => [
          'type' => 'string',
          'description' => 'Error details if loading failed',
        ],
      ],
    ];
  }

  /**
   * {@inheritdoc}
   */
  public function validateParams(array $params): ValidationResult {
    if (empty($params['context_id'])) {
      return ValidationResult::error('context_id', 'AI Context Item is required', 'MISSING_CONTEXT_ID');
    }
    return ValidationResult::success();
  }

  /**
   * {@inheritdoc}
   */
  public function process(ParameterBagInterface $params): array {
    $contextIdStr = $params->getString('context_id');
    $contextId = (int) $contextIdStr;

    if ($contextId <= 0) {
      return $this->failureResult($this->failureFields($contextIdStr), 'Invalid context item ID');
    }

    $storage = $this->entityTypeManager->getStorage('ai_context_item');
    $entity = $storage->load($contextId);

    if ($entity === NULL) {
      return $this->failureResult($this->failureFields($contextIdStr), "AI Context Item $contextId not found");
    }

    if (!$entity->isPublished()) {
      return $this->failureResult($this->failureFields($contextIdStr), "AI Context Item $contextId is not published");
    }

    if (!$entity->access('view')) {
      return $this->failureResult($this->failureFields($contextIdStr), "Access denied to AI Context Item $contextId");
    }

    $renderResult = $this->renderer->render(
      [$contextId],
      AiContextRequest::DEFAULT_MAX_TOKENS,
      [$contextId => $entity],
    );

    $this->recordContextUsage([$contextIdStr]);

    return $this->successResult([
      'rendered_text' => $renderResult['text'],
      'content' => $entity->getContent(),
      'purpose' => $entity->getPurpose(),
      'label' => (string) $entity->label(),
      'context_id' => $contextIdStr,
    ]);
  }

  /**
   * Builds the node-specific output fields for a failure result.
   *
   * @param string $contextIdStr
   *   The raw context_id parameter value.
   *
   * @return array<string, mixed>
   *   Empty output fields matching getOutputSchema(), minus the envelope.
   */
  private function failureFields(string $contextIdStr): array {
    return [
      'rendered_text' => '',
      'content' => '',
      'purpose' => '',
      'label' => '',
      'context_id' => $contextIdStr,
    ];
  }

}
