<?php
declare(strict_types=1);

namespace WP_AUX\Services;

use WP_AUX\Entities\ShortcodesEntity;
use WP_AUX\Services\AuxCF\BlocksService;

/**
 * Service for managing and rendering shortcodes
 * @since 1.0.0
 */
class ShortcodeService
{
  /**
   * Get an AUX shortcodes information from the shortcode file
   * @since 1.0.0
   */
  public static function getShortcodeData($shortcodeFile): array
  {
    $dataFields = [
      'Name' => 'Name',
      'Description' => 'Description',
      'Alias' => 'Alias',
    ];

    return get_file_data($shortcodeFile, $dataFields);
  }

  /**
   * Register AUX shortcodes from files in the shortcodes directory
   * @since 1.0.0
   */
  public static function registerShortcodes(): void
  {
    $shortcodes = [];

    $tryDirectories = \WP_AUX::tryDirectories('shortcodes');

    foreach ($tryDirectories as $tryDir) {
      $shortcodesFiles = @glob($tryDir . '/*.php');

      foreach ($shortcodesFiles as $shortcodeFile) {
        $shortcodeName = pathinfo($shortcodeFile, PATHINFO_FILENAME);

        if (isset($shortcodes[$shortcodeName])) {
          continue;
        }

        $shortcodes[$shortcodeName] = ShortcodesEntity::instance()->preregisterShortcode($shortcodeFile);
      }
    }

    ShortcodesEntity::instance()->addShortcodes();
  }

  /**
   * Render shortcode info HTML comment for site admins
   * @since 1.0.0
   */
  public static function renderComment(string $shortcodeFile): void
  {
    if (is_feed()) {
      return;
    }

    $shortcodeName = pathinfo($shortcodeFile, PATHINFO_FILENAME);
    $shortcodeData = self::getShortcodeData($shortcodeFile);

    $relativePath = str_replace(
      WP_CONTENT_DIR . '/themes/',
      '',
      $shortcodeFile
    );

    $comments = [
      'Shortcode: ' . $shortcodeName,
      'Name: ' . $shortcodeData['Name'],
      'Description: ' . $shortcodeData['Description'],
      'File Path: ' . $relativePath,
    ];

    echo "<!--\n" . join(";\n", $comments) . "\n-->";
  }

  /**
   * Render an AUX shortcode
   *
   * The $attributes and $content variables will be available to the AUX shortcode file
   *
   * @since 1.0.0
   */
  public static function doShortcode(
    array $attributes,
    string $content = '',
    string $shortcodeName = ''
  ): string
  {
    $renderContent = '';

    $filePath = ShortcodesEntity::instance()->getShortcodeFile($shortcodeName);

    if (!empty($filePath) && file_exists($filePath)) {
      ob_start();

      if (is_current_user_admin()) {
        self::renderComment($filePath);
      }

      include $filePath;

      $renderContent = ob_get_clean();
    }

    return $renderContent;
  }

  /**
   * Check shortcode for wrapping in a section
   *
   * AUX shortcodes are never wrapped in a section (any sections would be provided in
   * the shortcode file). Global shortcodes are wrapped in a section if they are
   * outside of an AUX block AND if section wrapping for the specific shortcode tag
   * is not disabled.
   *
   * To force section wrapping (or not section wrapping) outside of an AUX component,
   * use the 'aux-section-wrap' parameter in any shortcode.
   *
   * <pre>[shortcode aux-section-wrap="false"]Shortcode content[/shortcode]</pre>
   *
   * @since 1.0.1
   * @see self::doShortcodeWrap()
   */
  public static function doShortcodeWrap(string $content, string $shortcodeName = '', ?array $args = []) : string
  {
    $auxShortcodesHandler = ShortcodesEntity::instance();

    if (
      $auxShortcodesHandler->isAuxShortcode($shortcodeName) ||
      \WP_AUX::instance()->getCurrentBlockHandler()
    ) {
      return $content;
    }

    if (isset($args['aux-section-wrap'])) {
      $doSectionWrap = filter_var($args['aux-section-wrap'], FILTER_VALIDATE_BOOLEAN);
    } else {
      $globalShortcodeData = $auxShortcodesHandler->getGlobalShortcodeData($shortcodeName);
      $doSectionWrap = @$globalShortcodeData['section_wrap'] ?? true;
    }

    if ($doSectionWrap) {
      $section = BlocksService::getSection('', false);
      $section->setContent($content);
      $section->addSectionDataValue('shortcode', $shortcodeName);
      $content = (string) $section;
    }

    return $content;
  }

  /**
   * Set default shortcodes options for all global (non-AUX) shortcodes
   *
   * By default, all global shortcodes should be wrapped in a section when they are
   * used outside of AUX components.
   *
   * To disable section wrapping for a specific shortcode...
   *
   * <code>
   *  add_action('init', function() {
   *    ShortcodeService::registerShortcodeSectionWrap('shortcode-tag', false);
   *  });
   * </code>
   *
   * @since 1.0.1
   */
  public static function setupGlobalShortcodesOptions() : void
  {
    global $shortcode_tags;

    $auxShortcodesHandler = ShortcodesEntity::instance();

    foreach ($shortcode_tags as $shortcodeName => $shortcode) {
      if (!$auxShortcodesHandler->isAuxShortcode($shortcodeName)) {
        $globalData = $auxShortcodesHandler->getGlobalShortcodeData($shortcodeName);

        if (!isset($globalData['section_wrap'])) {
          self::registerShortcodeSectionWrap($shortcodeName, true);
        }
      }
    }
  }

  /**
   * Register whether or not to wrap a specific non-AUX shortcode in a section wrap
   * @since 1.0.1
   */
  public static function registerShortcodeSectionWrap(string $shortcodeName, bool $doWrap): void
  {
    ShortcodesEntity::instance()->addGlobalShortcodeData($shortcodeName, ['section_wrap' => $doWrap]);
  }

  /**
   * Render the [box][/box] shortcode
   * @since 1.0.0
   */
  public static function boxShortcode(array $attr, string $content = ''): string
  {
    return HtmlService::paragraph($content);
  }

  /**
   * Prepare Wordpress actions and filters for shortcodes
   * @since 1.0.0
   */
  public static function prepareHooks(): void
  {
    /**
     * Add AUX shortcodes
     */
    add_action('init', function() {
      self::registerShortcodes();
      add_shortcode('box', [ self::class, 'boxShortcode' ]);
    });

    /**
     * Set up default options for global (non-AUX) shortcodes
     */
    add_action('wp_loaded', [ self::class, 'setupGlobalShortcodesOptions']);

    /**
     * Register the shortcode section wrapper check
     */
    add_filter('do_shortcode_tag', [ self::class, 'doShortcodeWrap' ], 10, 3);
  }
}
