<?php

/**
 * Builds WordPress Playground blueprints for built-artifact file trees.
 *
 * @package LolaCore\Builder\Playground
 * @license GPL-2.0-or-later
 */

declare(strict_types=1);

namespace LolaCore\Builder\Playground;

// phpcs:disable WordPress.Security.EscapeOutput.ExceptionNotEscaped -- Diagnostic exception messages; escaped at REST/chat presentation layer.

use LolaCore\Builder\Artifact\FilesMap;

if (! defined('ABSPATH')) {
	exit;
}

/**
 * Assembles Playground blueprints from a validated files map.
 *
 * Uses a fixed step template (login → consumer-provided dependency steps →
 * installPlugin literal:directory → activatePlugin). Only the slug, the file
 * tree, and the dependency steps vary per artifact.
 */
final class BlueprintBuilder {

	public const PLAYGROUND_BASE_URL = 'https://playground.wordpress.net/#';

	public const PLAYGROUND_BLUEPRINT_URL_BASE = 'https://playground.wordpress.net/?blueprint-url=';

	public const SCHEMA_URL = 'https://playground.wordpress.net/blueprint-schema.json';

	/** Practical URL fragment size limit for encoded blueprints. */
	public const MAX_ENCODED_BYTES = 2097152;

	/**
	 * Assemble a blueprint for an artifact tree.
	 *
	 * @param array<int, array<string, mixed>> $dependencySteps Pre-built steps
	 *        installed before the artifact (wp.org plugins, sandbox seeding).
	 * @return array<string, mixed> Blueprint array ready for JSON encoding.
	 */
	public function buildFor(
		string $slug,
		FilesMap $files,
		array $dependencySteps = array(),
		string $landingPage = '/wp-admin/plugins.php'
	): array {
		$blueprint = $this->blueprintTemplate($landingPage);

		foreach ($dependencySteps as $step) {
			$blueprint['steps'][] = $step;
		}

		$blueprint['steps'] = array_merge(
			$blueprint['steps'],
			$this->artifactSteps($slug, $files)
		);

		return $blueprint;
	}

	/**
	 * Encode a blueprint into a Playground hash URL (legacy / offline use).
	 *
	 * @param array<string, mixed> $blueprint
	 *
	 * @throws \Exception When the blueprint cannot be encoded or is too large.
	 */
	public function encodeHashUrl( array $blueprint ): string {
		$json = wp_json_encode($blueprint);
		if (! is_string($json) || $json === '') {
			throw new \Exception(__('Could not encode the Playground blueprint.', 'lolacore'));
		}

		// Playground decodes hash fragments with decodeURIComponent first (see resolve-blueprint-from-url).
		$encoded = rawurlencode($json);
		if (strlen($encoded) > self::MAX_ENCODED_BYTES) {
			throw new \Exception(__('This plugin is too large to test via URL.', 'lolacore'));
		}

		return self::PLAYGROUND_BASE_URL . $encoded;
	}

	/**
	 * Step installing and activating a wp.org plugin dependency.
	 *
	 * @return array<string, mixed>
	 */
	public function wordpressOrgPluginStep( string $slug ): array {
		return array(
			'step'       => 'installPlugin',
			'pluginData' => array(
				'resource' => 'wordpress.org/plugins',
				'slug'     => $slug,
			),
			'options'    => array(
				'activate' => true,
			),
		);
	}

	/**
	 * Normalize file contents before embedding in a Playground blueprint.
	 *
	 * JSON.parse in browsers treats U+2028/U+2029 as line breaks even inside strings.
	 */
	public static function sanitizeFileContent( string $content ): string {
		$content = str_replace(array( "\u{2028}", "\u{2029}" ), "\n", $content);

		return trim($content);
	}

	/**
	 * Fixed Playground blueprint shell — only steps are appended per artifact.
	 *
	 * @return array<string, mixed>
	 */
	private function blueprintTemplate( string $landingPage ): array {
		return array(
			'$schema'           => self::SCHEMA_URL,
			'landingPage'       => $landingPage,
			'preferredVersions' => array(
				'php' => '8.3',
				'wp'  => 'latest',
			),
			'features'          => array(
				'networking' => true,
			),
			'steps'             => array(
				array(
					'step' => 'login',
				),
			),
		);
	}

	/**
	 * Write the artifact tree to disk, then activate by main file path.
	 *
	 * installPlugin with options.activate on a literal:directory passes only the
	 * folder path to Playground's activator. A separate activatePlugin step with
	 * slug/slug.php is more reliable (matches Playground blueprint guidance).
	 *
	 * @return array<int, array<string, mixed>>
	 */
	private function artifactSteps( string $slug, FilesMap $files ): array {
		return array(
			array(
				'step'       => 'installPlugin',
				'pluginData' => array(
					'resource' => 'literal:directory',
					'name'     => $slug,
					'files'    => $files->all(),
				),
				'options'    => array(
					'activate'         => false,
					'targetFolderName' => $slug,
				),
			),
			array(
				'step'       => 'activatePlugin',
				'pluginPath' => "{$slug}/{$slug}.php",
				'pluginName' => $slug,
			),
		);
	}
}

