<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\api\Libraries\Content\Udf;

/**
 * Metadata UDF Content.
 *
 * Representation of document/frame metadata.
 * Metadata is an arbitrary set of key-value pairs that can
 * be set by the client. Metadata sent with a request
 * will be available in an unchanged state on document retrieval
 * from the API (see `Document::download()`). Translated
 * documents will share the same metadata object regardless
 * of the target language.
 *
 * It implements the ArrayAccess interface which allows objects of
 * this class to be used as typical PHP array, e.g.
 * ```
 * $metadata = new Metadata();
 * $metadata['key'] = $value;
 * ```
 */
final class Metadata implements \ArrayAccess, MetadataInterface {
  /**
   * Internal hash map of metadata key-value pairs.
   *
   * @var array
   */
  private array $hashMap;

  /**
   * Constructs a new Metadata object.
   *
   * @param array $data
   *   An array of key-value pairs.
   */
  public function __construct(array $data = []) {
    $this->hashMap = $data;
  }

  /**
   * Adds new key-value pair to a Metadata.
   *
   * @param mixed $key
   *   The key to add.
   * @param mixed $value
   *   The value to add.
   */
  public function add(mixed $key, mixed $value): void {
    $this->offsetSet($key, $value);
  }

  /**
   * Removes a key-value pair for specified key.
   *
   * @param mixed $key
   *   The key to remove.
   */
  public function remove(mixed $key): void {
    $this->offsetUnset($key);
  }

  /**
   * Dehydrates object data into a serializable format.
   *
   * @return array
   *   An array of key-value pairs.
   */
  public function dehydrate(): array {
    return $this->hashMap;
  }

  /**
   * Whether an offset exists.
   *
   * @param mixed $offset
   *   An offset to check for.
   *
   * @return bool
   *   Returns true on success or false on failure.
   *
   * @todo Update signature when PHP 7.x support is dropped.
   */
  public function offsetExists(mixed $offset): bool {
    return array_key_exists($offset, $this->hashMap);
  }

  /**
   * Offset to retrieve.
   *
   * @param mixed $offset
   *   The offset to retrieve.
   *
   * @return mixed
   *   Can return all value types.
   *
   * @throws \InvalidArgumentException
   *   If the offset does not exist.
   *
   * @todo Update signature when PHP 7.x support is dropped
   */
  public function offsetGet(mixed $offset): mixed {
    if ($this->offsetExists($offset)) {
      return $this->hashMap[$offset];
    }

    throw new \InvalidArgumentException('Trying to access undefined key ' . $offset);
  }

  /**
   * Offset to set.
   *
   * @param mixed $offset
   *   The offset to set.
   * @param mixed $value
   *   The value to set.
   *
   * @todo Update signature when PHP 7.x support is dropped
   */
  public function offsetSet(mixed $offset, mixed $value): void {
    $this->hashMap[$offset] = $value;
  }

  /**
   * Offset to unset.
   *
   * @param mixed $offset
   *   The offset to unset.
   *
   * @throws \InvalidArgumentException
   *   If the offset does not exist.
   *
   * @todo Update signature when PHP 7.x support is dropped
   */
  public function offsetUnset(mixed $offset): void {
    if ($this->offsetExists($offset)) {
      unset($this->hashMap[$offset]);
      return;
    }

    throw new \InvalidArgumentException('Cannot unset offset. Key ' . $offset . ' not found');
  }

}
