<?php

declare(strict_types=1);

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

/**
 * Universal Document Factory class.
 *
 * It creates {@link UniversalDocument} object instance
 * from provided data.
 */
final class UniversalDocumentFactory {

  /**
   * Creates an instance of Universal Document Interface object.
   *
   * Full `$textData` array format:
   * ```
   * [
   *     'comment' => 'a full UDF frame', //optional
   *     'metadata' => [  //optional
   *         'foo' => 'bar',
   *         'baz' => 'bak'
   *     ],
   *    'preview' => 'https://example.com',
   *    'frames' => [
   *        [
   *            'text' => 'Hello World',
   *            'comment' => 'Frame comment', //optional
   *            'metadata' => [   //optional
   *                'a' => 'b',
   *                'c' => 'd'
   *            ]
   *        ]
   *    ]
   * ];
   * ```
   * Simple `$textData` array:
   * ```
   * ['Text 1', 'Text 2'];
   * ```
   * Simple `$textData` array with comments and metadata
   * (comment and metadata are optional):
   * ```
   * [
   *     [
   *        'text' => 'Text 1',
   *        'comment' => 'Comment 1',
   *        'metadata' => ['key 1' => 'val 1']
   *     ],
   *     [
   *        'text' => 'Text 2',
   *        'comment' => 'Comment 2',
   *        'metadata' => ['key 2' => 'val 2']
   *     ],
   * ];
   * ```.
   *
   * @param array $textData
   *   An array of text data.
   *
   * @return UniversalDocument
   *   An instance of Universal Document Interface object.
   *
   * @throws FrameMissingTextException.
   *   Thrown when a frame is missing text.
   */
  public static function create(array $textData): UniversalDocument {
    $framesCollection = self::createFramesCollection($textData);

    return new UniversalDocument(
          $framesCollection,
          $textData['comment'] ?? '',
          isset($textData['metadata']) ? new Metadata($textData['metadata']) : NULL,
          isset($textData['preview']) ? new Uri($textData['preview']) : NULL
      );
  }

  /**
   * Creates a collection of Frame objects based on text data.
   *
   * @param array $textData
   *   An array of text data.
   *
   * @return Frame[]
   *   An array of Frame objects.
   *
   * @throws FrameMissingTextException.
   *   Thrown when a frame is missing text.
   */
  private static function createFramesCollection(array $textData): array {
    $frames = $textData['frames'] ?? $textData;

    return array_map(function ($frameText): Frame {
        return self::createFrame($frameText);
    }, $frames);
  }

  /**
   * Creates Frame objects based on input data.
   *
   * @param array|string $frameText
   *   A string or an array of frame text data.
   *
   * @return Frame
   *   A Frame object.
   */
  private static function createFrame(array|string $frameText): Frame {
    if (is_string($frameText)) {
      return new Frame($frameText);
    }

    self::validateFrameData($frameText);

    return new Frame(
          $frameText['text'],
          $frameText['comment'] ?? '',
          isset($frameText['metadata']) ? new Metadata($frameText['metadata']) : NULL
      );
  }

  /**
   * Validates Frame data.
   *
   * @param array $frameText
   *   An array of frame text data.
   *
   * @throws FrameMissingTextException.
   *   Thrown when a frame is missing text.
   */
  private static function validateFrameData(array $frameText): void {
    if (!isset($frameText['text'])) {
      throw new FrameMissingTextException('Cannot create document without a text');
    }
  }

}
