<?php

/**
 * @file
 * WP API shims for synced block-supports functions.
 *
 * These shims bridge the gap between WordPress global functions/classes and
 * Drupal's dependency injection, allowing the upstream gutenberg_render_*
 * functions to work unmodified.
 *
 * Loaded before block-supports/*.php via StyleEngine.php require chain.
 */

use Drupal\gutenberg\Html\TagProcessor;

// --- WP_HTML_Tag_Processor class alias ---

if ( ! class_exists( 'WP_HTML_Tag_Processor' ) ) {
  class_alias( TagProcessor::class, 'WP_HTML_Tag_Processor' );
}

// --- WP_Block_Type: minimal value object ---

if ( ! class_exists( 'WP_Block_Type' ) ) {
  /**
   * Minimal WP_Block_Type stand-in.
   *
   * The upstream render functions access $block_type->supports. This wraps
   * the array returned by BlocksLibraryManager::getBlockDefinition() so that
   * property access works.
   */
  class WP_Block_Type {
    /** @var array */
    public $supports = array();

    /** @var string */
    public $name = '';

    /** @var array */
    public $attributes = array();

    public function __construct( $name, $definition = array() ) {
      $this->name       = $name;
      $this->supports   = isset( $definition['supports'] ) ? $definition['supports'] : array();
      $this->attributes = isset( $definition['attributes'] ) ? $definition['attributes'] : array();
    }
  }
}

// --- WP_Block_Type_Registry: singleton that delegates to Drupal's BlocksLibraryManager ---

if ( ! class_exists( 'WP_Block_Type_Registry' ) ) {
  class WP_Block_Type_Registry {
    private static $instance;

    public static function get_instance() {
      if ( ! self::$instance ) {
        self::$instance = new self();
      }
      return self::$instance;
    }

    /**
     * Get a registered block type by name.
     *
     * @param string $name Block type name (e.g. 'core/group').
     * @return WP_Block_Type|null Block type object or null.
     */
    public function get_registered( $name ) {
      /** @var \Drupal\gutenberg\BlocksLibraryManager $manager */
      $manager    = \Drupal::service( 'plugin.manager.gutenberg.blocks_library' );
      $definition = $manager->getBlockDefinition( $name );
      if ( ! $definition ) {
        return null;
      }
      return new \WP_Block_Type( $name, $definition );
    }
  }
}

// --- block_has_support(): check if a block type supports a feature ---

if ( ! function_exists( 'block_has_support' ) ) {
  /**
   * Checks whether a block type supports a given feature.
   *
   * @param WP_Block_Type $block_type    Block type object.
   * @param array         $feature       Path to the feature in supports array.
   * @param mixed         $default_value Default value if feature not found.
   * @return bool Whether the feature is supported.
   */
  function block_has_support( $block_type, $feature, $default_value = false ) {
    $block_support = $default_value;
    if ( $block_type && property_exists( $block_type, 'supports' ) ) {
      $block_support = _wp_array_get( $block_type->supports, $feature, $default_value );
    }
    return true === $block_support || is_array( $block_support );
  }
}

// --- sanitize_title(): simplified WP title sanitizer for CSS class usage ---

if ( ! function_exists( 'sanitize_title' ) ) {
  /**
   * Sanitizes a string into a slug-like format suitable for CSS classes.
   *
   * Simplified version of WP's sanitize_title_with_dashes(). Sufficient for
   * the layout support use cases (orientation names, layout classnames, block names).
   *
   * @param string $title The title to sanitize.
   * @return string Sanitized title.
   */
  function sanitize_title( $title ) {
    $title = strip_tags( $title );
    $title = strtolower( $title );
    // Remove HTML entities.
    $title = preg_replace( '/&.+?;/', '', $title );
    // Replace dots with dashes.
    $title = str_replace( '.', '-', $title );
    // Remove anything that's not alphanumeric, space, dash, or underscore.
    $title = preg_replace( '/[^a-z0-9 _-]/', '', $title );
    // Replace whitespace with dashes.
    $title = preg_replace( '/\s+/', '-', $title );
    // Collapse multiple dashes.
    $title = preg_replace( '/-+/', '-', $title );
    // Trim dashes from ends.
    $title = trim( $title, '-' );
    return $title;
  }
}

// --- gutenberg_get_global_settings(): return theme settings in upstream format ---

if ( ! function_exists( 'gutenberg_get_global_settings' ) ) {
  /**
   * Returns the global settings from the active theme.
   *
   * Maps Drupal's theme definition structure to the format expected by upstream
   * block-supports functions.
   *
   * @return array Global settings array.
   */
  function gutenberg_get_global_settings() {
    /** @var \Drupal\gutenberg\GutenbergLibraryManager $library_manager */
    $library_manager         = \Drupal::service( 'plugin.manager.gutenberg.library' );
    $active_theme_definitions = $library_manager->getActiveThemeDefinitions();
    $theme_definitions       = isset( reset( $active_theme_definitions )['theme-support'] )
      ? reset( $active_theme_definitions )['theme-support']
      : array();

    // Upstream expects settings at the top level (e.g., 'spacing.blockGap'),
    // but Drupal theme definitions may have them under '__experimentalFeatures'.
    $settings = $theme_definitions;
    if ( isset( $theme_definitions['__experimentalFeatures'] ) ) {
      $settings = array_merge( $settings, $theme_definitions['__experimentalFeatures'] );
    }

    // Expose globalStyles.spacing.blockGap as settings.spacing.blockGap when
    // not already set via __experimentalFeatures.spacing.blockGap.
    // gutenberg_render_layout_support_flag() reads $settings['spacing']['blockGap']
    // to determine $has_block_gap_support; without this, flex/grid layout blocks
    // never generate gap CSS for their .wp-container-* class.
    if (
      ! isset( $settings['spacing']['blockGap'] ) &&
      isset( $theme_definitions['globalStyles']['spacing']['blockGap'] )
    ) {
      if ( ! isset( $settings['spacing'] ) ) {
        $settings['spacing'] = array();
      }
      $settings['spacing']['blockGap'] = $theme_definitions['globalStyles']['spacing']['blockGap'];
    }

    return $settings;
  }
}

// --- wp_should_skip_block_supports_serialization(): always false in Drupal ---

if ( ! function_exists( 'wp_should_skip_block_supports_serialization' ) ) {
  /**
   * Checks whether serialization of block supports should be skipped.
   *
   * In Drupal context, we never skip serialization.
   *
   * @return bool Always false.
   */
  function wp_should_skip_block_supports_serialization( $block_type, ...$args ) {
    return false;
  }
}

// --- current_theme_supports(): theme feature support check ---

if ( ! function_exists( 'current_theme_supports' ) ) {
  /**
   * Checks whether the current theme supports a given feature.
   *
   * For layout rendering, the relevant check is 'disable-layout-styles'
   * which we never want to be true in Drupal context.
   *
   * @param string $feature The feature to check.
   * @return bool Whether the feature is supported.
   */
  function current_theme_supports( $feature ) {
    // 'disable-layout-styles' should return false so layout styles are generated.
    if ( 'disable-layout-styles' === $feature ) {
      return false;
    }
    return true;
  }
}

// --- wp_theme_has_theme_json(): whether the active theme uses theme.json ---

if ( ! function_exists( 'wp_theme_has_theme_json' ) ) {
  /**
   * Whether the active theme has a theme.json file.
   *
   * In Drupal context, Gutenberg themes always have theme.json-like
   * definitions, so we return true.
   *
   * @return bool Always true.
   */
  function wp_theme_has_theme_json() {
    return true;
  }
}

// --- wp_parse_args(): merge user args with defaults ---

if ( ! function_exists( 'wp_parse_args' ) ) {
  function wp_parse_args( $args, $defaults = array() ) {
    if ( is_string( $args ) ) {
      parse_str( $args, $parsed );
      $args = $parsed;
    }
    return array_merge( $defaults, (array) $args );
  }
}

// --- _doing_it_wrong(): developer debug notice (no-op in Drupal) ---

if ( ! function_exists( '_doing_it_wrong' ) ) {
  function _doing_it_wrong( $function_name, $message, $version ) {
    // No-op: upstream uses this for developer warnings only.
  }
}

// --- WP_Block_Supports: singleton registry for block support features ---

if ( ! class_exists( 'WP_Block_Supports' ) ) {
  /**
   * Minimal WP_Block_Supports stand-in.
   *
   * Upstream files call register() at load time to register support features.
   * In Drupal, block supports are applied via BlockProcessor classes, so this
   * is a no-op sink.
   */
  class WP_Block_Supports {
    private static $instance;

    public static function get_instance() {
      if ( ! self::$instance ) {
        self::$instance = new self();
      }
      return self::$instance;
    }

    public function register( $name, $args = array() ) {
      // No-op: Drupal applies block supports via BlockProcessor classes.
    }
  }
}

// --- add_filter() / remove_filter(): WordPress hook system (no-op in Drupal) ---

if ( ! function_exists( 'add_filter' ) ) {
  function add_filter( $hook_name, $callback, $priority = 10, $accepted_args = 1 ) {
    // No-op: Drupal does not use the WordPress hook system.
    return true;
  }
}

if ( ! function_exists( 'remove_filter' ) ) {
  function remove_filter( $hook_name, $callback, $priority = 10 ) {
    // No-op: Drupal does not use the WordPress hook system.
    return true;
  }
}

// --- esc_attr(): HTML attribute escaping ---

if ( ! function_exists( 'esc_attr' ) ) {
  /**
   * Escapes a string for safe use in an HTML attribute.
   *
   * @param string $text The string to escape.
   * @return string Escaped string.
   */
  function esc_attr( $text ) {
    return htmlspecialchars( (string) $text, ENT_QUOTES, 'UTF-8' );
  }
}

// --- wp_enqueue_script_module(): no-op in Drupal (ES module loading) ---

if ( ! function_exists( 'wp_enqueue_script_module' ) ) {
  /**
   * Enqueues a JavaScript module.
   *
   * No-op in Drupal: upstream uses this (e.g. for fit-text frontend) but
   * Drupal loads view scripts via its own library system.
   */
  function wp_enqueue_script_module( ...$args ) {
    // No-op: Drupal handles script loading via libraries.
  }
}

// --- _deprecated_argument(): deprecated argument notice (no-op in Drupal) ---

if ( ! function_exists( '_deprecated_argument' ) ) {
  function _deprecated_argument( $function_name, $version, $message = '' ) {
    // No-op: upstream uses this for deprecation warnings only.
  }
}
