<?php
declare(strict_types=1);

namespace WP_AUX\Services;

use WP_AUX\Entities\AuxFramework\Partials\DismissibleTagCollectionEntity;
use WP_AUX\Entities\Search\SearchSidebarEntity;

/**
 * Provides various search-related functionalities, including taxonomy options,
 * dismissible tags, and handling of search parameters in sidebar searches.
 *
 * @since 1.0.0
 */
class SearchService
{
  /**
   * Get options for terms in a taxonomy
   *
   * If no specific terms are selected, the top 5 most used (within the current post type on archives)
   * will be provided.
   *
   * @since 1.0.0
   */
  public static function getTaxonomyOptions(array $fieldTerms, ?string $postType): array
  {
    $taxonomySlug = explode('--', $fieldTerms['taxonomy'])[1];
    $taxonomy = get_taxonomy($taxonomySlug);

    $optionsData = $fieldTerms[$fieldTerms['taxonomy']];

    if (empty($optionsData)) {
      $optionsData = TaxonomyTermsService::getMostUsedTerms($taxonomy->name, $postType, 5);
    } else {
      // Filter out Uncategorized category from manually selected terms
      if ( 'category' === $taxonomy->name && is_array( $optionsData ) ) {
        $optionsData = array_filter( $optionsData, function( $term ) {
          if ( is_object( $term ) && isset( $term->slug ) ) {
            return $term->slug !== 'uncategorized';
          }
          if ( is_numeric( $term ) ) {
            // If term ID is provided, check the slug
            $term_obj = get_term( $term );
            if ( $term_obj && ! is_wp_error( $term_obj ) && isset( $term_obj->slug ) ) {
              return $term_obj->slug !== 'uncategorized';
            }
          }
          return true;
        } );
        $optionsData = array_values( $optionsData );
      }
    }

    return [
      'fieldName' => $taxonomy->query_var,
      'queryVarName' => $taxonomy->query_var,
      'optionsData' => $optionsData,
    ];
  }

  /**
   * Get an array list of all the dismissible tags for the current search
   * @since 1.0.0
   */
  protected static function getDismissibleTags(): array
  {
    $dismissibleTags = [];

    $searchSidebar = SearchSidebarEntity::instance();
    $sidebarSections = $searchSidebar->getSections();
    $searchedParams = $searchSidebar->getSearchedParams();

    if (!empty($searchedParams['text'])) {
      $urlParams = [
        's' => '',
        'filter' => $searchedParams['filter'],
      ];

      $dismissibleTags[] = [
        'filter_type' => 'text',
        'name' => 'text',
        'value' => $searchedParams['text'],
        'title' => $searchedParams['text'],
        'section_label' => '',
        'field_label' => '',
        'query' => http_build_query($urlParams),
      ];
    }

    foreach ($sidebarSections as $sidebarSection) {
      foreach ($sidebarSection['filter'] as $filterField) {
        foreach ($filterField['options'] as $optionValue => $option) {
          if ($option['selected']) {
            $urlParams = [
              's' => $searchedParams['text'],
              'filter' => $searchedParams['filter'],
            ];

            switch ($sidebarSection['filter_type']) {
              case 'checkbox':
                $removeParamIndex = array_search($optionValue, $urlParams['filter'][$filterField['name']]);
                unset($urlParams['filter'][$filterField['name']][$removeParamIndex]);
                break;
              case 'radio':
              case 'select':
                unset($urlParams['filter'][$filterField['name']]);
                break;
            }

            $dismissibleTags[] = [
              'filter_type' => $sidebarSection['filter_type'],
              'name' => $filterField['name'],
              'value' => $optionValue,
              'title' => $option['label'],
              'section_label' => $sidebarSection['heading'],
              'field_label' => $filterField['label'],
              'query' => http_build_query($urlParams),
            ];
          }
        }
      }
    }

    if (!empty($dismissibleTags)) {
      foreach ($dismissibleTags as $tagKey => $tag) {
        $dismissibleTags[$tagKey]['url'] = $searchSidebar->getUrl() . ($tag['query'] ? '?' . $tag['query'] : '');
        $dismissibleTags[$tagKey]['data'] = [
          'field-type' => $tag['filter_type'],
          'field-name' => $tag['name'],
        ];

        if ('text' !== $tag['filter_type']) {
          $dismissibleTags[$tagKey]['data']['field-value'] = $tag['value'];
        }
      }
    }

    return $dismissibleTags;
  }

  /**
   * Values for search sidebar fields are populated, and attributes are prepared in this method.
   * @since 1.0.0
   */
  public static function prepareSearchSections(array $sections, ?string $postType): array
  {
    foreach ($sections as $sectionKey => $section) {
      if (empty($section['filter'])) {
        unset($sections[$sectionKey]);
        continue;
      }

      foreach ($section['filter'] as $fieldKey => $field) {
        $fieldSource = $field['source'] ?? 'taxonomy';
        $options = [];

        switch ($fieldSource) {
          case 'post_type':
            $optionsData = $field['post_types'];
            $fieldName = 'post_type';
            $queryVarName = 'post_type';
            break;
          case 'taxonomy':
          default:
            $taxonomyOptions = self::getTaxonomyOptions($field['terms'], $postType);

            $optionsData = $taxonomyOptions['optionsData'];
            $fieldName = $taxonomyOptions['fieldName'];
            $queryVarName = $taxonomyOptions['queryVarName'];
            break;
        }

        if (!$optionsData) {
          unset($sections[$sectionKey]['filter'][$fieldKey]);
          continue;
        }

        $selected = get_query_var($queryVarName);
        if (is_string($selected)) {
          $selected = explode(',', $selected);
        }

        $sections[$sectionKey]['filter'][$fieldKey]['name'] = $fieldName;
        $sections[$sectionKey]['filter'][$fieldKey]['input_name'] = 'filter[' . $fieldName . ']';

        if ('checkbox' === $section['filter_type']) {
          $sections[$sectionKey]['filter'][$fieldKey]['input_name'] .= '[]';
        }

        foreach ($optionsData as $option) {
          switch ($fieldSource) {
            case 'taxonomy':
              $optionValue = $option->slug;
              $optionLabel = $option->name;
              break;
            case 'post_type':
              $optionValue = $option;
              $optionLabel = PostService::getPostTypeLabel($option);
              break;
          }

          $options[$optionValue] = [
            'label' => $optionLabel,
            'selected' => in_array($optionValue, $selected),
          ];
        }

        $sections[$sectionKey]['filter'][$fieldKey]['options'] = $options;
      }

      if (empty($sections[$sectionKey]['filter'])) {
        unset($sections[$sectionKey]);
        continue;
      }
    }

    return $sections;
  }

  /**
   * Get the dismissible tags content
   *
   * If there are tags, a DismissibleTagCollectionEntity is returned. Otherwise an empty string is returned.
   *
   * @since 1.0.0
   */
  public static function getDismissibleTagsContent(): string|DismissibleTagCollectionEntity
  {
    $dismissibleCollection = '';

    $dismissibleTags = self::getDismissibleTags();

    if (!empty($dismissibleTags)) {
      $dismissibleCollection = DismissibleTagCollectionEntity::create('Showing results for:');

      foreach ($dismissibleTags as $tag) {
        $dismissibleCollection->addItem($tag);
      }
    }

    return $dismissibleCollection;
  }

  /**
   * Load scripts and add search parameters to the query for sidebar searches
   * @since 1.0.0
   */
  public static function prepareHooks(): void
  {
    add_action('wp_enqueue_scripts', function() {
      if (!is_admin() && is_search() && is_main_query()) {
        HeadService::enqueueLocalScript('aux-search', 'js/aux-search.js');
      }
    });

    add_action('pre_get_posts', function(\WP_Query $query) {
      if (
        is_admin() ||
        !$query->is_search() ||
        !$query->is_main_query()
      ) {
        return;
      }

      $filterParams = filter_input(INPUT_GET, 'filter', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);

      if (!empty($filterParams)) {
        foreach ($filterParams as $filterParamKey => $filterParamValue) {
          if ('any' === $filterParamValue) {
            continue;
          }
          
          $query->set($filterParamKey, $filterParamValue);
        }
      }
    });
  }
}
