<?php

declare(strict_types=1);

namespace Drupal\acquia_dam\TypedData;

use Drupal\acquia_dam\Plugin\media\Source\Asset;
use Drupal\Core\Cache\CacheableDependencyInterface;
use Drupal\Core\Cache\CacheableDependencyTrait;
use Drupal\canvas\Entity\ParametrizedImageStyle;
use Drupal\canvas\Plugin\DataType\UriTemplate;
use Drupal\file\FileInterface;
use Drupal\media\MediaInterface;
use Drupal\media\MediaTypeInterface;

/**
 * Computes URI template with a `{width}` variable to populate `<img srcset>`.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/srcset#value
 * @see https://tools.ietf.org/html/rfc6570
 * @internal
 */
final class AssetDerivativeWithParametrizedWidth extends UriTemplate implements CacheableDependencyInterface {

  use CacheableDependencyTrait;

  private function getParametrizedImageStyle(): ParametrizedImageStyle {
    // @phpstan-ignore-next-line
    return ParametrizedImageStyle::load('canvas_parametrized_width');
  }

  /**
   * {@inheritdoc}
   */
  public function getValue() {
    if ($this->getParent() === NULL || $this->getParent()->getParent() === NULL) {
      return NULL;
    }

    $asset = $this->getParent()->getEntity();

    assert($asset->getSource() instanceof Asset);
    assert($asset->bundle->entity instanceof MediaTypeInterface);
    assert($asset->getSource()->getDerivativeId() === 'image');

    $source = $asset->getSource();
    $source_configuration = $asset->bundle->entity->get('source_configuration');
    $isDownloadAssets = $source_configuration['download_assets'] ?? FALSE;

    $url_template = NULL;
    if ($isDownloadAssets) {
      $field = $asset->get($source->getLocalFileAssetField());
      $file = $field->first()->get('entity')?->getTarget()?->getValue();
      if (!$field->isEmpty() && $file instanceof FileInterface) {
        $uri = $file->getFileUri();
        $url_template = $this->getParametrizedImageStyle()->buildUrlTemplate($uri);
      }
    }

    $url_template = $url_template ?? $this->createTemplateUrl($asset, $source);

    assert(str_contains($url_template, '{width}'));

    return $url_template;
  }

  public function getCastedValue(): string {
    return $this->getValue();
  }

  public function getCacheTags() {
    return $this->getParametrizedImageStyle()->getCacheTags();
  }

  public function getCacheContexts() {
    return $this->getParametrizedImageStyle()->getCacheContexts();
  }

  public function getCacheMaxAge() {
    return $this->getParametrizedImageStyle()->getCacheMaxAge();
  }


  /**
   * Replace template variables in a given template string.
   *
   * @param string $template
   *   The template string containing variables to be replaced.
   * @param array $replacements
   *   An associative array where keys are the variables to be replaced (including
   *   curly braces) and values are the corresponding replacement values.
   *
   * @return string
   *   The template string with variables replaced by their corresponding values.
   */
  private function replaceTemplateVariables(string $template, array $replacements): string {
    return str_replace(array_keys($replacements), array_values($replacements), $template);
  }

  /**
   * Constructs the templated URL for an asset using its metadata.
   *
   * @param \Drupal\media\MediaInterface $asset
   *   The media entity representing the asset.
   * @param \Drupal\acquia_dam\Plugin\media\Source\Asset $source
   *   The media source plugin instance associated with the asset.
   *
   * @return string
   *   The constructed templated URL for the asset.
   */
  private function createTemplateUrl(MediaInterface $asset, Asset $source): string {
    $domain = \Drupal::config('acquia_dam.settings')->get('domain');
    assert($domain !== NULL, 'The Acquia DAM domain should be configured.');
    $domain = strtok($domain, '.');
    $template = "https://{domain}.widen.net/content/{external_id}/{format}/{filename}?q=100&w={width}";
    assert($template !== NULL, 'The asset should have a templated embed URL.');
    return $this->replaceTemplateVariables($template, [
      '{domain}' => $domain,
      '{external_id}' => $source->getMetadata($asset, 'external_id'),
      '{format}' => strtolower($source->getMetadata($asset, 'format')),
      '{filename}' => $source->getMetadata($asset, 'default_name'),
    ]);
  }

}
