<?php

namespace ModernFields\FieldGroups\Locations\UseCases;

use ModernFields\FieldGroups\Entities\FieldGroup;
use ModernFields\FieldGroups\Locations\UseCases\GetEditorStylesUseCase;
use ModernFields\WordPress\Services\RenderApiService;
use ModernFields\WordPress\Services\ScriptDataService;

use WP_Block_Editor_Context;

/**
 * Use case class responsible for loading all assets required for field groups.
 *
 * This service handles:
 * - Enqueuing common WordPress assets (media library, common styles)
 * - Loading location-specific JavaScript and CSS bundles
 * - Injecting field group data and editor settings into JavaScript
 * - Adding dynamic inline styles for field groups
 *
 * @since 1.0.0
 */
class GroupAssetsLoaderUseCase
{
  protected string $plugin_dir;
  protected string $plugin_url;

  /**
   * Constructor.
   *
   * @since 1.0.0
   * @param string $folder The build folder name (e.g., 'post-fields', 'term-fields').
   * @param array<int, FieldGroup> $groups Array of FieldGroup entities to load assets for.
   * @param array $context Additional context data to pass to the JavaScript.
   */
  public function __construct(
    protected string $folder = "",
    protected array $groups = [],
    protected array $context = [],
  ) {
    $this->plugin_dir = MODERN_FIELDS_PLUGIN_DIR;
    $this->plugin_url = MODERN_FIELDS_PLUGIN_URL;
  }

  /**
   * Main entry point to load all assets for field groups.
   *
   * Orchestrates the complete asset loading process by calling:
   * 1. Common assets (media library, base styles)
   * 2. Location-specific assets (JS/CSS bundles)
   * 3. Inline assets (group data, editor settings, dynamic styles)
   *
   * @since 1.0.0
   * @return void
   */
  public function execute(): void
  {
    if (!$this->folder || empty($this->groups)) {
      return;
    }

    $this->enqueueCommonAssets();
    $this->enqueueInteractiveBlockAssets();
    $this->enqueueLocationSpecificAssets();
    $this->inlineScripts();
    $this->inlineStyles();
  }

  /**
   * Enqueues the lightweight accordion runtime when a field group renders a
   * block that relies on the Interactivity API (e.g. the core Accordion block).
   *
   * Field group content is server-rendered through the_content but injected
   * client-side after page load, so the core Interactivity runtime — which only
   * hydrates on DOMContentLoaded — never wires these blocks up. We ship a tiny
   * vanilla runtime instead. The bundle always lives in the free plugin, even
   * when this loader runs from the premium plugin, so it is referenced through
   * the free plugin constants.
   *
   * @since 1.0.2
   * @return void
   */
  protected function enqueueInteractiveBlockAssets(): void
  {
    if (!$this->groupsContainInteractiveBlock()) {
      return;
    }

    $file_path = MODERN_FIELDS_PLUGIN_DIR . 'build/accordion/index.asset.php';

    if (!file_exists($file_path)) {
      return;
    }

    $asset_file = require $file_path;

    wp_enqueue_script(
      'modern-fields-accordion',
      MODERN_FIELDS_PLUGIN_URL . 'build/accordion/index.js',
      $asset_file['dependencies'],
      $asset_file['version'],
      true
    );
  }

  /**
   * Determines whether any field group renders an Interactivity-API block.
   *
   * Detection relies on the `data-wp-interactive` marker that WordPress injects
   * into interactive block roots while rendering the_content. The same marker is
   * what the core client runtime scans for, so it is guaranteed to be present in
   * the rendered output whenever an interactive block is used.
   *
   * @since 1.0.2
   * @return bool True when at least one group contains an interactive block.
   */
  protected function groupsContainInteractiveBlock(): bool
  {
    foreach ($this->groups as $group) {
      if (str_contains($group->getContent(), 'data-wp-interactive')) {
        return true;
      }
    }

    return false;
  }

  /**
   * Enqueues common WordPress assets required by all field group locations.
   *
   * This includes:
   * - WordPress media library scripts (for media upload fields)
   * - Common block fields CSS (shared styles for all block-based fields)
   * - Field groups layout CSS (location-specific layout overrides)
   *
   * @since 1.0.0
   * @return void
   */
  public function enqueueCommonAssets(): void
  {
    # Enqueue WordPress media library
    wp_enqueue_media();

    # Expose window.modernFieldsFree so third-party field add-ons render through
    # the same engine (FieldWrapper, useFieldValue…) as native fields.
    RenderApiService::enqueue();

    # Include common styles for the block fields
    wp_enqueue_style(
      "modern-fields-blocks-fields",
      MODERN_FIELDS_PLUGIN_URL . 'assets/css/blocks-fields.css',
      [],
      MODERN_FIELDS_VERSION
    );

    # Specific layout overrides for field groups
    wp_enqueue_style(
      "modern-fields-field-groups-layout",
      MODERN_FIELDS_PLUGIN_URL . 'assets/css/field-groups-location-layouts.css',
      [],
      MODERN_FIELDS_VERSION
    );
  }

  /**
   * Enqueues location-specific JavaScript and CSS assets.
   *
   * Loads the compiled JavaScript and CSS bundles from the build directory
   * for the specific location (e.g., post metabox, options page, user fields).
   * The asset file (index.asset.php) contains dependency and version information
   * generated by the build process.
   *
   * @since 1.0.0
   * @return void
   */
  public function enqueueLocationSpecificAssets(): void
  {
    $folder = $this->folder;

    # Path to the asset file containing dependencies and version info
    $file_path = $this->plugin_dir . "build/$folder/index.asset.php";

    if (!file_exists($file_path)) {
      return;
    }

    $asset_file = require_once $file_path;

    # Enqueue the main JavaScript bundle with dependencies from build process
    wp_enqueue_script(
      'modern-fields-' . $folder,
      $this->plugin_url . "build/$folder/index.js",
      $asset_file['dependencies'],
      $asset_file['version'],
      true
    );

    wp_set_script_translations('modern-fields-' . $folder, 'modern-fields');

    # Enqueue the main CSS bundle if it exists
    $css_path = $this->plugin_dir . "build/$folder/index.css";
    if (file_exists($css_path)) {
      wp_enqueue_style(
        'modern-fields-' . $folder,
        $this->plugin_url . "build/$folder/index.css",
        ['wp-components'],
        MODERN_FIELDS_VERSION
      );
    }
  }

  /**
   * Injects block editor settings, field groups data, and dynamic styles for use in JavaScript.
   *
   * This method performs the following tasks:
   * - Collects block editor settings from WordPress (including custom color palettes, etc.)
   * - Converts FieldGroup entities to associative array format keyed by group ID
   * - Localizes the main script with editor settings and field groups data (accessible via `modernFields` global)
   * - Gathers all dynamic CSS required for the field groups and outputs it inline
   *
   * The localized data is available in JavaScript as `window.modernFields` containing:
   * - `editorSettings`: WordPress block editor configuration
   * - `groups`: Associative array of field groups, keyed by group ID
   *
   * @since 1.0.0
   * @return void
   */
  public function inlineScripts(): void
  {
    # Get the block editor settings (for font sizes, spacing, etc. outside the block editor)
    $block_editor_context = new WP_Block_Editor_Context(['name' => 'modern-fields']);
    $editor_settings_full = get_block_editor_settings([], $block_editor_context);

    # get_block_editor_settings() misses theme.json colors outside the block editor context,
    # so we fetch the full palette (default + theme + custom) directly from theme.json
    $palette = wp_get_global_settings(['color', 'palette']);
    $colors = array_merge(
      $palette['theme']   ?? [],
      $palette['custom']  ?? [],
    );

    # Keep only what we need in front (for JS pages outside the block editor)
    $editor_settings = [
      'colors' => $colors,
      'spacingSizes' => $editor_settings_full['spacingSizes'],
    ];

    # Convert FieldGroup entities to associative array format keyed by group ID
    $groups_array = [];
    foreach ($this->groups as $group) {
      $groups_array[$group->getId()] = $group->toArray();
    }

    ScriptDataService::enqueue([
      'editorSettings' => $editor_settings,
      'groups'         => $groups_array,
      'context'        => $this->context,
    ]);
  }

  /**
   * Collects all CSS used by the field groups and hands it to the client-side
   * scoper.
   *
   * The CSS is generated unscoped: scoping is applied in the browser via
   * wp.blockEditor.transformStyles, which correctly remaps WordPress root
   * selectors (:root, body) to the .modern-fields-content-area container.
   * This avoids the @scope pitfalls where body-prefixed rules never match.
   *
   * @since 1.0.0
   * @return void
   */
  public function inlineStyles(): void
  {
    # Collect all dynamic CSS styles used across all field groups, unscoped
    $getEditorStylesUseCase = new GetEditorStylesUseCase($this->groups, false);
    $styles = $getEditorStylesUseCase->execute();

    if (empty($styles)) {
      return;
    }

    $this->enqueueStylesScoper();

    if (!wp_script_is('modern-fields-styles-scoper', 'enqueued')) {
      return;
    }

    # Hand the raw CSS to the scoper, accumulating across locations/groups.
    wp_add_inline_script(
      'modern-fields-styles-scoper',
      'window.mfRawStyles=(window.mfRawStyles||[]);window.mfRawStyles.push(' . wp_json_encode($styles) . ');',
      'before'
    );
  }

  /**
   * Enqueues the client-side style scoper bundle (idempotent).
   *
   * The bundle always lives in the free plugin, even when this loader runs from
   * the premium plugin, so it is referenced through the free plugin constants.
   *
   * @since 1.0.0
   * @return void
   */
  protected function enqueueStylesScoper(): void
  {
    if (wp_script_is('modern-fields-styles-scoper', 'enqueued')) {
      return;
    }

    $file_path = MODERN_FIELDS_PLUGIN_DIR . 'build/styles-scoper/index.asset.php';

    if (!file_exists($file_path)) {
      return;
    }

    $asset_file = require $file_path;

    wp_enqueue_script(
      'modern-fields-styles-scoper',
      MODERN_FIELDS_PLUGIN_URL . 'build/styles-scoper/index.js',
      $asset_file['dependencies'],
      $asset_file['version'],
      true
    );
  }
}
