<?php
/**
 * AUX theme utility and shorthand functions
 *
 * @since 1.0.0
 */

declare(strict_types=1);

use AuxCF\Entities\FieldEntity;
use WHOOP\Entities\Elements\Ul;
use WP_AUX\Entities\AuxFramework\Partials\AlertEntity;
use WP_AUX\Entities\AuxFramework\Partials\HeaderSearchEntity;
use WP_AUX\Entities\AuxFramework\Partials\HeaderUtilityCtasEntity;
use WP_AUX\Entities\AuxFramework\Partials\HeaderUtilityPersonaNavEntity;
use WP_AUX\Entities\AuxFramework\Partials\HeadingGroupEntity;
use WP_AUX\Entities\AuxFramework\Partials\QuicklinksEntity;
use WP_AUX\Entities\AuxCF\OptionsFields\PostType\PostTypeOptionsFeaturedImageFieldEntity;
use WP_AUX\Entities\AuxCF\PageFields\Taxonomy\TaxonomyFeaturedImageFieldEntity;
use WP_AUX\Entities\PageTemplateEntity;
use WP_AUX\Entities\PostsQuery\PostsQueryEntity;
use WP_AUX\Entities\PostsQuery\RecentPostsQueryEntity;
use WP_AUX\Entities\PostsQuery\RelatedPostsQueryEntity;
use WP_AUX\Entities\Search\SearchSidebarEntity;
use WP_AUX\Enum\AlertTypeEnum;
use WP_AUX\Enum\SiteHeaderVariationEnum;
use WP_AUX\Services\AdminService;
use WP_AUX\Services\AuxFramework\ElementClassesService;
use WP_AUX\Services\AuxCF\FieldsService;
use WP_AUX\Services\HtmlService;
use WP_AUX\Services\PostService;
use WP_AUX\Services\PostTypeService;
use WP_AUX\Services\TaxonomyTermsService;

/**
 * Set the version control URL of a child theme
 *
 * Use this function inside the 'after_setup_theme' action in your child theme.
 *
 * <code>
 *   add_action('after_setup_theme', function() {
 *     aux_theme_version_control('https://site.tamu.edu/wordpress/child-theme/my-theme/theme.json');
 *   });
 * </code>
 *
 * @since 1.0.0
 * @uses \WP_AUX\Entities\ThemeEntity::setChildVersionControlUrl()
 */
function aux_theme_version_control(string $versionControlUrl): void
{
  \WP_AUX::theme()->setChildVersionControlUrl($versionControlUrl);
}

/**
 * Set the theme/site identifier
 *
 * Use this function inside the 'after_setup_theme' action in your child theme.
 *
 * <code>
 *   add_action('after_setup_theme', function() {
 *     aux_theme_set_identifier('my-site');
 *   });
 * </code>
 *
 * @since 1.0.0
 * @uses \WP_AUX\Entities\ThemeEntity::setIdentifier()
 */
function aux_theme_set_identifier(string $identifier): void
{
  \WP_AUX::theme()->setIdentifier($identifier);
}

/**
 * Set the pattern for permalink URLs in the theme, ensuring correct setup of URLs
 *
 * Use this function inside the 'after_setup_theme' action in your child theme.
 *
 * <code>
 *   add_action('after_setup_theme', function() {
 *     aux_theme_set_permalink_pattern('/%postname%/');
 *   });
 * </code>
 *
 * @since 1.0.0
 * @uses \WP_AUX\Entities\ThemeEntity::setPermalinkPattern()
 */
function aux_theme_set_permalink_pattern(string $permalinkPattern): void
{
  \WP_AUX::theme()->setPermalinkPattern($permalinkPattern);
}

/**
 * Remove a menu from the theme before it is registered
 *
 * The AUX parent theme provides a primary menu (menu-1) by default. Every site
 * should have at least one menu. If a child theme does not have a menu, the
 * primary menu (menu-1) can be removed with this function in an
 * 'after_setup_theme' action in the child theme.
 *
 * @since 1.0.0
 * @uses \WP_AUX\Entities\ThemeEntity::removeMenu()
 */
function aux_theme_remove_menu(string $menu): void
{
  \WP_AUX::theme()->removeMenu($menu);
}

/**
 * Get a setting for the current page from the page builder
 *
 * @since 1.0.0
 * @uses \WP_AUX\Entities\PageTemplateEntity::getSetting()
 */
function aux_page_builder_setting(string $settingKey): mixed
{
  $setting = PageTemplateEntity::getRequested()->getSetting($settingKey);

  if ('' === $setting) {
    return null;
  }

  return $setting;
}

/**
 * Get all of the header settings
 * @since 1.0.0
 */
function aux_header_settings(): array
{
  $auxHeader = get_option('aux_header');

  if (!$auxHeader) {
    $auxHeader = [];
  }

  if (!isset($auxHeader['header_variation'])) {
    $auxHeader['header_variation'] = SiteHeaderVariationEnum::DEFAULT->value;
  }

  return $auxHeader;
}

/**
 * Get the header variation
 * @since 1.0.0
 */
function aux_header_variation(): SiteHeaderVariationEnum
{
  $auxHeader = aux_header_settings();

  return SiteHeaderVariationEnum::tryFrom($auxHeader['header_variation']) ?? SiteHeaderVariationEnum::DEFAULT;
}

/**
 * Get the name of the search string field
 * @since 1.0.0
 */
function aux_search_field_name(): string
{
  return apply_filters('aux/search_field_name', 's');
}

/**
 * Render an AUX alert banner if the post is not published to the public.
 * @since 1.0.0
 */
function aux_unpublished_banner(): void
{
  $post = get_post();

  if (!$post) {
    return;
  }

  $unpublishedStatus = false;
  switch ($post->post_status) {
    case 'draft':
      $unpublishedStatus = 'This post is a draft and is not visible to the public.';
      break;
    case 'private':
      $unpublishedStatus = 'This post is private and is not visible to the public.';
      break;
    case 'future':
      $unpublishedStatus = 'This post is scheduled to publish in the future and is not currently visible to the public.';
      break;
    case 'pending':
      $unpublishedStatus = 'This post is pending publish and is not visible to the public.';
      break;
  }

  if (!empty($post->post_password)) {
    if (!empty($unpublishedStatus)) {
      $unpublishedStatus .= ' This post is also password protected.';
    } else {
      $unpublishedStatus .= 'This post is password protected.';
    }
  }

  if (!empty($unpublishedStatus)) {
    echo AlertEntity::createBanner(
      AlertTypeEnum::Error,
      'Unpublished Content',
      $unpublishedStatus
    );
  }
}

/**
 * Get the identity lockup HTML
 *
 * The lockup name is managed in the "Customizer", under "AUX Header Options"
 *
 * @since 1.0.0
 */
function aux_header_utility_back(): string
{
  $siteHeaderVariation = aux_header_variation();

  $collegeLink = '';
  if ($siteHeaderVariation->utilityHasCollege() && @aux_header_settings()['college_name']) {
    $collegeLink = sprintf(
      '|<a href="%s" class="site-header__department-link">%s</a>',
      @aux_header_settings()['college_url'] ?? '#',
      aux_header_settings()['college_name']
    );
  }

  $backClasses = ElementClassesService::buildClasses('site-header__back');
  $backClasses->addModifier($siteHeaderVariation->value);

  ob_start();
  ?>
  <div class="<?php echo $backClasses; ?>">
    <a href="https://www.tamu.edu/index.html" class="site-header__parent-link">
      <svg><use href="#aux_arrow-up-right"></use></svg>Texas A&amp;M University</a><?php echo $collegeLink; ?>
  </div>
  <?php
  return ob_get_clean();
}

/**
 * Get the utility Quicklinks HTML content
 *
 * Use the 'aux/header/quicklinks' filter to change the Quicklinks before rendering.
 *
 * <code>
 *   add_filter('aux/header/quicklinks', function($quicklinks) {
 *     $quicklinks->addLink([
 *       'title' => 'Link 1',
 *       'url' => '/page/',
 *       'description' => 'This is a link to a page about things',
 *     ]);
 *   });
 * </code>
 *
 * @since 1.0.0
 */
function aux_header_utility_quicklinks(): string
{
  $quickLinks = QuicklinksEntity::create();

  if ($quicklinkItems = aux_get_menu_items('aux-header-utility-quicklinks')) {
    foreach ($quicklinkItems as $quicklink) {
      $quickLinks->addLink([
        'title' => $quicklink->title,
        'url' => $quicklink->url,
        'description' => $quicklink->description,
      ]);
    }
  }

  apply_filters('aux/header/quicklinks', $quickLinks);

  return (string) $quickLinks;
}

/**
 * Get the utility Persona HTML content
 *
 * Use the 'aux/header/persona' filter to change the Persona menu before rendering.
 *
 * <code>
 *   add_filter('aux/header/persona', function($persona) {
 *     $persona->addLink([
 *       'title' => 'Link 1',
 *       'url' => '/page/',
 *     ]);
 *   });
 * </code>
 *
 * @since 1.0.0
 */
function aux_header_utility_persona(): string
{
  $persona = HeaderUtilityPersonaNavEntity::create();

  if ($personaItems = aux_get_menu_items('aux-header-utility-persona')) {
    foreach ($personaItems as $personaItem) {
      $persona->addLink([
        'title' => $personaItem->title,
        'url' => $personaItem->url,
      ]);
    }
  }

  apply_filters('aux/header/persona', $persona);

  return (string) $persona;
}

/**
 * Get the utility CTAs HTML content
 *
 * Use the 'aux/header/ctas' filter to change the CTAs before rendering.
 *
 * <code>
 *   add_filter('aux/header/ctas', function($ctas) {
 *     $ctas->addLink([
 *       'title' => 'Link 1',
 *       'url' => '/page/',
 *     ]);
 *   });
 * </code>
 *
 * @since 1.0.0
 */
function aux_header_utility_ctas(bool $mobile = false): string
{
  $ctas = HeaderUtilityCtasEntity::create($mobile);

  if ($ctasItems = aux_get_menu_items('aux-header-utility-ctas')) {
    foreach ($ctasItems as $cta) {
      $ctas->addLink([
        'title' => $cta->title,
        'url' => $cta->url,
      ]);
    }
  }

  apply_filters('aux/header/ctas', $ctas);

  return (string) $ctas;
}

/**
 * Get menu items by menu location name
 * @since 1.0.0
 */
function aux_get_menu_items(string $menuName): array|false
{
  $menu = false;

  if ($menuLocations = get_nav_menu_locations()) {
    $menu = get_term(@$menuLocations[$menuName], 'nav_menu');
  }

  return $menu ? wp_get_nav_menu_items($menu) : false;
}


/**
 * Get the header utility nav search form HTML
 *
 * Search can be hidden in the "Customizer", under "AUX Header Options"
 *
 * @since 1.0.0
 */
function aux_header_utility_search(): string
{
  if (@aux_header_settings()['hide_search']) {
    return '';
  }

  $searchForm = HeaderSearchEntity::create('utility');

  apply_filters('aux/header/search_form', $searchForm);

  return (string) $searchForm;
}

/**
 * Get the header identity section search form HTML
 *
 * Search can be hidden in the "Customizer", under "AUX Header Options"
 *
 * @since 1.0.0
 */
function aux_header_identity_search(): string
{
  if (@aux_header_settings()['hide_search']) {
    return '';
  }

  $searchForm = HeaderSearchEntity::create('identity');

  apply_filters('aux/header/search_form', $searchForm);

  return (string) $searchForm;
}

/**
 * Get the heaer identity lockup HTML
 *
 * The lockup name is managed in the "Customizer", under "AUX Header Options"
 *
 * @since 1.0.0
 */
function aux_identity_lockup(bool $stacked = false): string
{
  $lockupName = esc_html(@aux_header_settings()['lockup_name']);
  if ($lockupNameAdditional = @aux_header_settings()['lockup_name_additional']) {
    $lockupName .= '<br>' . $lockupNameAdditional;
  }

  $identityClasses = ElementClassesService::buildClasses('identity');
  if ($stacked) {
    $identityClasses->addModifier('stacked');
  }

  ob_start();
  ?>
  <div class="<?php echo $identityClasses; ?>">
    <a href="<?php echo get_home_url(); ?>">
      <div class="identity__logo">
        <img alt="Texas A&amp;M University logo" src="https://aux.tamu.edu/logos/boxTAM.svg">
      </div>
      <div class="identity__wordmark">
        <span class="wordmark__small">Texas A&amp;M University</span>
        <span class="wordmark__large">
          <?php echo $lockupName; ?>
        </span>
      </div>
    </a>
  </div>
  <?php
  return ob_get_clean();
}

/**
 * Get related posts
 *
 * Manual provision of related posts is supported. "Expansion" of related posts
 * is provided if not enough related posts are found.
 *
 * @param string[]|null $postTypes
 * @return \WP_Post[]
 * @since 1.0.0
 * @uses \WP_AUX\Entities\PostsQuery\RelatedPostsQueryEntity::create()
 */
function aux_related_posts(
  array $taxonomies,
  ?array $postTypes = null,
  int $limit = 3
): PostsQueryEntity
{
  if (is_null($postTypes)) {
    $post = get_post();
    $postTypes = [ $post->post_type ];
  }

  $relatedPosts = RelatedPostsQueryEntity::create($taxonomies, $postTypes, $limit);

  return $relatedPosts;
}

/**
 * Get recent posts
 *
 * "Expansion" of recent posts is provided if not enough related posts are found.
 *
 * @param string|string[]|null $postTypes
 * @return \WP_Post[]
 * @since 1.0.0
 * @uses \WP_AUX\Entities\PostsQuery\RecentPostsQueryEntity::create()
 */
function aux_recent_posts(array|string|null $postTypes, array $taxonomies, int $limit = 3): RecentPostsQueryEntity
{
  if (empty($postTypes)) {
    $postTypes = get_post_types(
      [
        'exclude_from_search' => false,
        'has_archive' => true,
      ],
      'names'
    );
  } else {
    $postTypes = (array) $postTypes;
  }

  $recentPosts = RecentPostsQueryEntity::create($taxonomies, $postTypes, $limit);

  return $recentPosts;
}

/**
 * Get main page heading group background image for current archive
 * @since 1.0.0
 */
function aux_archive_heading_background(
  ?string $defaultBackgroundPath = null,
  ?string $defaultBackgroundAlt = null
): string
{
  $backgroundPath = '';
  $backgroundTitle = '';

  $queriedObject = get_queried_object();

  if ($queriedObject instanceof \WP_Term) {
    $image = TaxonomyTermsService::getTermField(
      TaxonomyFeaturedImageFieldEntity::$name,
      $queriedObject->term_id
    );

    $backgroundPath = @$image['url'];
    $backgroundTitle = @$image['alt'];
  } elseif ($queriedObject instanceof \WP_Post_Type) {
    $image = PostTypeService::getArchiveOption(
      $queriedObject->name,
      PostTypeOptionsFeaturedImageFieldEntity::class
    );

    if (!empty($image)) {
      $backgroundPath = @$image['url'];
      $backgroundTitle = @$image['alt'];
    }
  }

  if (!$backgroundPath && $defaultBackgroundPath) {
    $backgroundPath = get_stylesheet_directory_uri() . '/' . $defaultBackgroundPath;
    $backgroundTitle = $defaultBackgroundAlt;
  }

  return HtmlService::formattedValue(
    $backgroundPath,
    '<div class="page-header__image"><img src="%s" alt="%s"></div>',
    $backgroundTitle
  );
}

/**
 * Get main page heading group for current archive
 * @since 1.0.0
 */
function aux_archive_heading_group(string|array|null $additionalStyles = null): HeadingGroupEntity
{
  $header = '';
  $superheader = '';

  /* Get main header and superheader from Wordpress default */
  if (preg_match(
    '/(.*)\<span\>(.*)\<\/span\>/',
    get_the_archive_title(),
    $matches)
  ) {
    $header = $matches[2];
  }

  $headingGroup = HeadingGroupEntity::create($header);
  $headingGroup->setLevel(1);
  $headingGroup->addStyle('display');
  $headingGroup->setSuperheader($superheader);

  if (!empty($additionalStyles)) {
    $additionalStyles = (array) $additionalStyles;
    foreach ($additionalStyles as $style) {
      $headingGroup->addStyle($style);
    }
  }

  return $headingGroup;
}

/**
 * Get main page heading group for current search
 * @since 1.0.0
 */
function aux_search_heading_group(string|array|null $additionalStyles = null): HeadingGroupEntity
{
  $header = get_search_query();
  $superheader = 'Search Results';

  $postType = SearchSidebarEntity::instance()->getPostType();

  if ($header) {
    if ($postType) {
      $superheader .= ' in ' . PostService::getPostTypeLabel($postType);
    }

    $superheader .= ' for';
  } elseif ($postType) {
    $superheader .= ' in';
    $header = PostService::getPostTypeLabel($postType);
  } else {
    $header = $superheader;
    $superheader = '';
  }

  $headingGroup = HeadingGroupEntity::create($header);
  $headingGroup->setLevel(1);
  $headingGroup->addStyle('display');
  $headingGroup->setSuperheader($superheader);

  if (!empty($additionalStyles)) {
    $additionalStyles = (array) $additionalStyles;
    foreach ($additionalStyles as $style) {
      $headingGroup->addStyle($style);
    }
  }

  return $headingGroup;
}

/**
 * Remove External/Homepage URL field from category and tag taxonomies
 * @since 1.0.0
 */
add_action('acf/get_fields', function($fields, $parent) {
  if (
    is_tag() ||
    is_category() ||
    AdminService::isTaxonomyEditor('category') ||
    AdminService::isTaxonomyEditor('post_tag')
  ) {
    FieldsService::removeFieldFromGroup($fields, 'aux_taxonomy_url');
  }

  return $fields;
}, 20, 2);

/**
 * Set TAMU favicon and icon metatags by default.
 * @since 1.0.0
 */
function aux_set_favicon()
{
  // get_template_directory_uri returns the parent theme path
  $icon_path = get_template_directory_uri() . '/images/tamu-icons';

  $icon_link_tags = <<<HTML
    <link rel="icon" sizes="32x32" href="$icon_path/favicon.ico" />
    <link rel="icon" sizes="any" type="image/svg+xml" href="$icon_path/TAM-maroon.svg" />
    <link rel="apple-touch-icon" sizes="180x180" href="$icon_path/TAM-maroon-180.png" />
    <link rel="icon" sizes="192x192" href="$icon_path/TAM-maroon-192.png" />
  HTML;

  echo $icon_link_tags;
}
add_action('login_head', 'aux_set_favicon');
add_action('admin_head', 'aux_set_favicon');
add_action('wp_head', 'aux_set_favicon');

/**
 * Check if current user agent is a specific browser
 * @since 1.0.0
 */
function aux_is_browser(string $browser): bool
{
  $browser = strtolower($browser);

  $regex = "#({$browser})[/ ]([0-9.]*)#i";

  if (preg_match($regex, $_SERVER['HTTP_USER_AGENT'], $matches)) {
    if ('safari' === $browser) {
      return !aux_is_browser('edg');
    }

    return true;
  }

  return false;
}

/**
 * Catch and redirect (if possible) incorrect URLs AFTER WordPress's default redirect
 * @since 1.0.0
 */
add_action('template_redirect', function() {
  if (is_404()) {
    $redirectUrl = false;

    $path = explode('/', trim($_SERVER['REQUEST_URI'], '/'));

    /*
     * Get post slug first, then post type (path may only be one item long).
     */
    $postSlug = array_pop($path);
    $postTypeSlug = array_shift($path);
    $requestedPostTypeName = null;

    /*
     * If there is a post type slug, test all rewritten post types for a matching slug.
     */
    if ($postTypeSlug) {
      $postTypes = get_post_types(['rewrite' => null], 'objects', 'not');

      foreach ($postTypes as $postType) {
        if (isset($postType->rewrite['slug']) && $postTypeSlug === $postType->rewrite['slug']) {
          $requestedPostTypeName = $postType->name;
          break;
        }
      }
    }

    /*
     * Test the post slug and maybe-null post type for a matching post, if not
     * and if there is a matching post type archive, redirect to that.
     */
    if ($post = get_page_by_path( $postSlug, OBJECT, $requestedPostTypeName )) {
      $redirectUrl = get_the_permalink($post);
    } elseif ($requestedPostTypeName) {
      $redirectUrl = get_post_type_archive_link($requestedPostTypeName);
    }

    /*
     * If a new, valid URL was found, go there.
     */
    if ($redirectUrl) {
      wp_redirect($redirectUrl);
    }
  }
});

/**
 * Add a notice to the permalinks admin page explaining the theme-controlled permalink pattern
 * @since 1.0.0
 */
add_action('all_admin_notices', function() {
  $screen = get_current_screen();

  if ('options-permalink' === $screen->id) {
    $content = <<<HTML
        <div class="notice notice-warning">
            <p>
            To ensure proper setup, while the AUX theme or a child of the AUX
            theme is active, the permalink structure is <code>%s</code>. This
            value can be changed in your child theme's functions.php using the
            <code>aux_theme_set_permalink_pattern()</code> function inside an
            <code>after_theme_setup</code> Wordpress action.
            </p>
        </div>
    HTML;

    echo sprintf(
      $content,
      WP_AUX::theme()->getPermalinkPattern()
    );
  }
});

/**
 * Set the Wordpress permalink pattern option from the theme
 * @since 1.0.0
 */
add_action('init', function() {
  global $wp_rewrite;

  $permalinkPattern = WP_AUX::theme()->getPermalinkPattern();

  if (!$permalinkPattern) {
    return;
  }

  $wp_rewrite->set_permalink_structure($permalinkPattern);
  $wp_rewrite->flush_rules();
});

/**
 * Remove wpautop from factoid fields
 * @since 1.0.0
 * @todo Target only the factoid content field
 */
add_action('aux/get_field/factoid', function(?FieldEntity $field) {
  remove_filter('acf_the_content', 'wpautop');
}, 10, 1);

/**
 * Restore the wpautop filter after removing it on factoid fields
 * @since 1.0.0
 */
add_action('aux/after_get_field/factoid', function(?FieldEntity $field) {
  add_filter('acf_the_content', 'wpautop');
}, 10, 1);

add_action('wp_nav_menu_args', function($args) {
  $args['depth'] = \WP_AUX::theme()->getMainNavDepth();
  return $args;
});
