<?php

namespace Wga\Core\ViewHelpers\Asset;

use TYPO3\CMS\Core\Page\AssetCollector;
use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractTagBasedViewHelper;

/**
 * This is XClass, but it is also used directly by namespace in the 500 error template.
 *
 * Class ScriptViewHelper.
 */
class ScriptViewHelper extends AbstractTagBasedViewHelper
{
	/**
	 * This VH does not produce direct output, thus does not need to be wrapped in an escaping node
	 *
	 * @var bool
	 */
	protected $escapeOutput = false;

	/**
	 * Rendered children string is passed as JavaScript code,
	 * there is no point in HTML encoding anything from that.
	 *
	 * @var bool
	 */
	protected $escapeChildren = false;

	protected AssetCollector $assetCollector;

	public function injectAssetCollector(AssetCollector $assetCollector): void
	{
		$this->assetCollector = $assetCollector;
	}

	public function initialize(): void
	{
		// Add a tag builder, that does not html encode values, because rendering with encoding happens in AssetRenderer
		$this->setTagBuilder(
			new class () extends TagBuilder {
				public function addAttribute($attributeName, $attributeValue, $escapeSpecialCharacters = false): void
				{
					parent::addAttribute($attributeName, $attributeValue, false);
				}
			}
		);
		parent::initialize();
	}

	public function initializeArguments(): void
	{
		parent::initializeArguments();
		$this->registerUniversalTagAttributes();
		$this->registerTagAttribute('async', 'bool', 'Define that the script will be fetched in parallel to parsing and evaluation.', false);
		$this->registerTagAttribute('crossorigin', 'string', 'Define how to handle crossorigin requests.', false);
		$this->registerTagAttribute('defer', 'bool', 'Define that the script is meant to be executed after the document has been parsed.', false);
		$this->registerTagAttribute('integrity', 'string', 'Define base64-encoded cryptographic hash of the resource that allows browsers to verify what they fetch.', false);
		$this->registerTagAttribute('nomodule', 'bool', 'Define that the script should not be executed in browsers that support ES2015 modules.', false);
		$this->registerTagAttribute('nonce', 'string', 'Define a cryptographic nonce (number used once) used to whitelist inline styles in a style-src Content-Security-Policy.', false);
		$this->registerTagAttribute('referrerpolicy', 'string', 'Define which referrer is sent when fetching the resource.', false);
		$this->registerTagAttribute('src', 'string', 'Define the URI of the external resource.', false);
		$this->registerTagAttribute('type', 'string', 'Define the MIME type (usually \'text/javascript\').', false);
		$this->registerArgument('useNonce', 'bool', 'Whether to use the global nonce value', false, false);
		$this->registerArgument(
			'identifier',
			'string',
			'Use this identifier within templates to only inject your JS once, even though it is added multiple times.',
			true
		);
		$this->registerArgument(
			'priority',
			'boolean',
			'Define whether the JavaScript should be put in the <head> tag above-the-fold or somewhere in the body part.',
			false,
			false
		);
	}

	public function render(): string
	{
		$identifier = (string)$this->arguments['identifier'];
		$attributes = $this->tag->getAttributes();

		// boolean attributes shall output attr="attr" if set
		foreach (['async', 'defer', 'nomodule'] as $_attr) {
			if ($attributes[$_attr] ?? false) {
				$attributes[$_attr] = $_attr;
			}
		}

		$src = $this->tag->getAttribute('src');
		unset($attributes['src']);
		$options = [
			'priority' => $this->arguments['priority'],
		];

		if (!empty($this->assetCollector)) {
			if ($src !== null) {
				$this->assetCollector->addJavaScript($identifier, $src, $attributes, $options);
			} else {
				$content = (string)$this->renderChildren();
				if ($content !== '') {
					$this->assetCollector->addInlineJavaScript($identifier, $content, $attributes, $options);
				}
			}
		}

		return '';
	}
}