<?php

declare(strict_types=1);

namespace Drupal\flowdrop\Service\Bundle;

/**
 * Signs a workflow bundle payload with a publisher's Ed25519 secret key.
 *
 * This is the publisher-side counterpart to BundleVerifier. A self-hosting
 * catalog or a developer can produce signed bundles with it; the FlowDrop
 * marketplace's production secret key lives in the commercial service, never in
 * this repository. The signature is detached and computed over the canonical
 * payload bytes, exactly as the verifier recomputes them.
 *
 * @internal
 */
final class BundleSigner {

  public function __construct(
    private readonly BundleCanonicalizer $canonicalizer,
  ) {}

  /**
   * Builds a signed bundle from a payload.
   *
   * @param array $payload
   *   The payload to sign. Its 'publisher' member must equal $keyId so the
   *   bundle the verifier sees is internally consistent.
   * @param string $keyId
   *   The publisher key id to stamp into the signature envelope.
   * @param string $secretKey
   *   The 64-byte raw Ed25519 secret key.
   *
   * @return array
   *   The bundle: 'format', 'payload', and a 'signature' envelope.
   *
   * @throws \InvalidArgumentException
   *   When the payload publisher does not match the key id.
   */
  public function sign(array $payload, string $keyId, string $secretKey): array {
    if (($payload['publisher'] ?? NULL) !== $keyId) {
      throw new \InvalidArgumentException('Payload publisher must match the signing key id.');
    }

    $message = $this->canonicalizer->canonicalize($payload);
    $signature = sodium_crypto_sign_detached($message, $secretKey);

    return [
      'format' => BundleVerifier::FORMAT,
      'payload' => $payload,
      'signature' => [
        'alg' => BundleVerifier::ALGORITHM,
        'key_id' => $keyId,
        'value' => base64_encode($signature),
      ],
    ];
  }

}
