<?php
declare(strict_types=1);

namespace WP_AUX\Entities\Search;

use WHOOP\Entities\Elements\Input;
use WP_AUX\Entities\AuxCF\OptionsFields\PostType\PostTypeSearchSectionFieldEntity;
use WP_AUX\Entities\AuxCF\OptionsFields\PostType\PostTypeSearchSwitchFieldEntity;
use WP_AUX\Entities\AuxCF\OptionsFields\SearchSidebar\SearchSidebarSectionFieldEntity;
use WP_AUX\Services\HtmlService;
use WP_AUX\Services\PostService;
use WP_AUX\Services\PostTypeService;
use WP_AUX\Services\SearchService;

/**
 * Handles the search sidebar layout and functionality
 *
 * Options and fields are configured in the admin panel under "Dashboard" > "Site Settings" > "Search"
 *
 * @since 1.0.0
 */
class SearchSidebarEntity
{
  protected bool $sidebarEnabled = true;
  protected array $searchedParams = [];
  protected string $url = '';
  protected ?string $postType = null;
  protected ?string $title = null;
  protected ?string $heading = null;
  protected array $sections = [];

  /**
   * Set the title, heading, and filter sections from Site Settings > Search
   *
   * There should only ever be one search sidebar, so we protect the constructor.
   * The search sidebar is accessed through the public static self::instance() method
   *
   * @since 1.0.0
   */
  protected function __construct()
  {
    if (is_tax() || is_tag() || is_category()) {
      $this->sidebarEnabled = false;
      return;
    }

    /*
     * The AUX theme already has strict alphanumeric-only filtering on
     * "get_search_query" in functions-front-end.php, so we skip WP filtering here
     * @Dion -
     */
    // Get 'filter' from the query string, unslash per WP conventions, and coerce to array
    $rawFilter = (array) wp_unslash($_GET['filter'] ?? []);
    // Sanitize all values recursively using WordPress' text sanitizer
    $sanitizedFilter = map_deep($rawFilter, 'sanitize_text_field');

    // Store the current search text and sanitized filter parameters
    $this->searchedParams = [
      'text' => get_search_query(false),
      'filter' => $sanitizedFilter,
    ];

    if (is_post_type_archive()) {
      $this->postType = get_query_var('post_type');

      $this->url = get_post_type_archive_link($this->postType);
      $this->title = 'Search ' . PostService::getPostTypeLabel($this->postType);

      $this->sidebarEnabled = PostTypeService::getSearchOption(
        $this->postType,
        PostTypeSearchSwitchFieldEntity::class,
        false
      );

      $sections = PostTypeService::getSearchOption($this->postType, PostTypeSearchSectionFieldEntity::class, []);
    } else {
      $this->url = get_home_url(null, '/');
      $this->title = get_field('aux_search_sidebar_title', 'option');
      $this->heading = get_field('aux_search_sidebar_heading', 'option');

      $sections = aux_get_field_array(SearchSidebarSectionFieldEntity::$name, [], 'option');
    }

    if ($sections) {
      $this->sections = SearchService::prepareSearchSections($sections, $this->postType);
    }
  }

  public function getSidebarEnabled(): bool
  {
    return $this->sidebarEnabled;
  }

  public function getPostType(): ?string
  {
    return $this->postType;
  }

  public function getUrl(): string
  {
    return $this->url;
  }

  public function getSearchedParams(): array
  {
    return $this->searchedParams;
  }

  /**
   * Get the content of the sidebar title in an H2 if a title is provided
   * @since 1.0.0
   */
  public function getTitleContent(): string
  {
    return HtmlService::formattedValue(
      $this->title,
      '<h2>%s</h2>'
    );
  }

  /**
   * Get the content of the sidebar heading (above the search text field) in an H2 if a title is provided
   * @since 1.0.0
   */
  public function getHeadingContent(): string
  {
    return HtmlService::formattedValue(
      $this->heading,
      '<h2 class="search__heading">%s</h2>'
    );
  }

  /**
   * Get the filter sections
   *
   * Sections configurations are prepared in SearchService::prepareSearchSections()
   *
   * @since 1.0.0
   */
  public function getSections(): array
  {
    return $this->sections;
  }

  /**
   * Get the search input field as an object
   * @since 1.0.0
   */
  public function getStringField(): Input
  {
    return Input::createSearch([
      'name' => aux_search_field_name(),
      'placeholder' => '',
      'id' => 'sidebar-search',
      'class' => 'search__input',
      'value' => get_search_query(false),
    ]);
  }

  /**
   * Render the search sidebar template part if the sidebar is enabled
   * @since 1.0.0
   */
  public function doSidebar(): void
  {
    if ($this->sidebarEnabled) {
      get_template_part('template-parts/search-sidebar');
    }
  }

  /**
   * Check if the sidebar has sections defined, even if the sections are empty
   * @since 1.0.0
   */
  public function hasSections(): bool
  {
    return !empty($this->sections);
  }

  /**
   * Access the one instance of the search sidebar, and create the instance if it hasn't been created yet
   * @since 1.0.0
   */
  public static function instance(): static
  {
    static $instance;

    if (is_null($instance)) {
      $instance = new static();
    }

    return $instance;
  }
}
