<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Integrations\Structural\Providers\WordPress;

use Ekesto\CacheInvalidator\ContentVisibility;
use Ekesto\CacheInvalidator\Support\Debug;
use Ekesto\CacheInvalidator\Integrations\Structural\Providers\Contracts\StructuralContainerProviderInterface;
use Ekesto\CacheInvalidator\Integrations\Structural\Core\StructuralContainerResolution;

/**
 * Structural provider for wp_template entities.
 *
 * Resolves deterministic template usage and marks unsupported hierarchy
 * contexts uncertain so callers can use their global fallback.
 */
final class TemplateProvider implements StructuralContainerProviderInterface
{
    /**
     * Return the provider key.
     *
     * @return string
     */
    public function key(): string
    {
        return 'wp_template';
    }

    /**
     * Return structural post types classified by this provider.
     *
     * @return string[]
     */
    public function structuralPostTypes(): array
    {
        return ['wp_template'];
    }

    /**
     * Determine whether this provider owns the provided post.
     *
     * @param \WP_Post $post Post object.
     * @return bool
     */
    public function supports(\WP_Post $post): bool
    {
        return $post->post_type === 'wp_template';
    }

    /**
     * Resolve one provider-owned usage step.
     *
     * @param int $postId Structural entity ID.
     * @return StructuralContainerResolution
     */
    public function resolveUsage(int $postId): StructuralContainerResolution
    {
        $post = get_post($postId);
        if (!$post instanceof \WP_Post || !$this->supports($post)) {
            return new StructuralContainerResolution([], [], false, false, false);
        }

        if ($post->post_status !== 'publish') {
            return new StructuralContainerResolution([], [], true, true, false);
        }

        $templateSlug = trim((string) $post->post_name);
        if ($templateSlug === '') {
            return new StructuralContainerResolution([], [], true, true, false);
        }

        $archiveTemplate = $this->detectArchiveTemplate($templateSlug);
        if ($archiveTemplate !== null) {
            Debug::log('wp_template archive template detected', [
                'post_id' => $postId,
                'template_slug' => sanitize_key($templateSlug),
                'archive_kind' => sanitize_key((string) ($archiveTemplate['kind'] ?? '')),
                'taxonomy' => sanitize_key((string) ($archiveTemplate['taxonomy'] ?? '')),
                'post_type' => sanitize_key((string) ($archiveTemplate['post_type'] ?? '')),
            ]);

            if (empty($archiveTemplate['supported'])) {
                $fallbackReason = sanitize_key((string) ($archiveTemplate['fallback_reason'] ?? 'unsupported_archive_template_slug'));
                Debug::log('wp_template archive template fallback', [
                    'post_id' => $postId,
                    'template_slug' => sanitize_key($templateSlug),
                    'archive_kind' => sanitize_key((string) ($archiveTemplate['kind'] ?? '')),
                    'taxonomy' => sanitize_key((string) ($archiveTemplate['taxonomy'] ?? '')),
                    'post_type' => sanitize_key((string) ($archiveTemplate['post_type'] ?? '')),
                    'term_count' => 0,
                    'url_count' => 0,
                    'fallback_reason' => $fallbackReason,
                ]);

                return new StructuralContainerResolution([], [], true, true, false, [], $fallbackReason);
            }

            $resolved = ($archiveTemplate['kind'] ?? '') === 'taxonomy_archive'
                ? $this->resolvePublicTaxonomyArchiveUrls((string) ($archiveTemplate['taxonomy'] ?? ''))
                : $this->resolvePostTypeArchiveUrl((string) ($archiveTemplate['post_type'] ?? ''));
            $termCount = (int) ($resolved['term_count'] ?? 0);
            $urls = is_array($resolved['urls'] ?? null) ? $resolved['urls'] : [];

            if (empty($resolved['ok'])) {
                $fallbackReason = sanitize_key((string) ($resolved['fallback_reason'] ?? 'unsupported_archive_template_slug'));
                Debug::log('wp_template archive template fallback', [
                    'post_id' => $postId,
                    'template_slug' => sanitize_key($templateSlug),
                    'archive_kind' => sanitize_key((string) ($archiveTemplate['kind'] ?? '')),
                    'taxonomy' => sanitize_key((string) ($archiveTemplate['taxonomy'] ?? '')),
                    'post_type' => sanitize_key((string) ($archiveTemplate['post_type'] ?? '')),
                    'term_count' => $termCount,
                    'url_count' => 0,
                    'fallback_reason' => $fallbackReason,
                ]);

                return new StructuralContainerResolution([], [], true, true, false, [], $fallbackReason);
            }

            Debug::log('wp_template archive template resolved', [
                'post_id' => $postId,
                'template_slug' => sanitize_key($templateSlug),
                'archive_kind' => sanitize_key((string) ($archiveTemplate['kind'] ?? '')),
                'taxonomy' => sanitize_key((string) ($archiveTemplate['taxonomy'] ?? '')),
                'post_type' => sanitize_key((string) ($archiveTemplate['post_type'] ?? '')),
                'term_count' => $termCount,
                'url_count' => count($urls),
                'fallback_reason' => '',
            ]);

            return new StructuralContainerResolution([], [], true, false, false, $urls, '');
        }

        $singleTemplate = $this->detectSingleTemplate($templateSlug);
        if ($singleTemplate !== null) {
            Debug::log('wp_template single template detected', [
                'post_id' => $postId,
                'template_slug' => sanitize_key($templateSlug),
                'single_kind' => sanitize_key((string) ($singleTemplate['kind'] ?? '')),
                'post_type' => sanitize_key((string) ($singleTemplate['post_type'] ?? '')),
                'post_slug' => sanitize_title((string) ($singleTemplate['post_slug'] ?? '')),
            ]);

            if (empty($singleTemplate['supported'])) {
                $fallbackReason = sanitize_key((string) ($singleTemplate['fallback_reason'] ?? 'unsupported_single_template_slug'));
                Debug::log('wp_template single template fallback', [
                    'post_id' => $postId,
                    'template_slug' => sanitize_key($templateSlug),
                    'single_kind' => sanitize_key((string) ($singleTemplate['kind'] ?? '')),
                    'post_type' => sanitize_key((string) ($singleTemplate['post_type'] ?? '')),
                    'post_slug' => sanitize_title((string) ($singleTemplate['post_slug'] ?? '')),
                    'target_count' => 0,
                    'fallback_reason' => $fallbackReason,
                ]);

                return new StructuralContainerResolution([], [], true, true, false, [], $fallbackReason);
            }

            $resolved = ($singleTemplate['kind'] ?? '') === 'specific_single'
                ? $this->resolveSpecificSingleTemplateTarget(
                    (string) ($singleTemplate['post_type'] ?? ''),
                    (string) ($singleTemplate['post_slug'] ?? '')
                )
                : $this->resolveSingleTemplateTargets((array) ($singleTemplate['post_types'] ?? []));

            $targetIds = is_array($resolved['post_ids'] ?? null) ? $resolved['post_ids'] : [];
            $postTypes = [];

            foreach ($targetIds as $id) {
                $pt = get_post_type($id);
                if (is_string($pt) && $pt !== '') {
                    $postTypes[$pt] = ($postTypes[$pt] ?? 0) + 1;
                }
            }
            if (empty($resolved['ok'])) {
                $fallbackReason = sanitize_key((string) ($resolved['fallback_reason'] ?? 'single_template_resolution_failed'));
                Debug::log('wp_template single template fallback', [
                    'post_id' => $postId,
                    'template_slug' => sanitize_key($templateSlug),
                    'single_kind' => sanitize_key((string) ($singleTemplate['kind'] ?? '')),
                    'post_type' => sanitize_key((string) ($singleTemplate['post_type'] ?? '')),
                    'post_slug' => sanitize_title((string) ($singleTemplate['post_slug'] ?? '')),
                    'target_count' => count($targetIds),
                    'fallback_reason' => $fallbackReason,
                ]);

                return new StructuralContainerResolution([], [], true, true, false, [], $fallbackReason);
            }

            Debug::log('wp_template single template resolved', [
                'post_id' => $postId,
                'template_slug' => sanitize_key($templateSlug),
                'single_kind' => sanitize_key((string) ($singleTemplate['kind'] ?? '')),
                'post_type' => sanitize_key((string) ($singleTemplate['post_type'] ?? '')),
                'post_slug' => sanitize_title((string) ($singleTemplate['post_slug'] ?? '')),
                'target_count' => count($targetIds),
                'post_types' => $postTypes,
                'fallback_reason' => '',
            ]);

            return new StructuralContainerResolution($targetIds, [], true, false, false);
        }

        if ($this->isResolvedHierarchyTemplate($post)) {
            $fallbackReason = 'unsupported_template_hierarchy';
            Debug::log('wp_template hierarchy fallback', [
                'post_id' => $postId,
                'template_slug' => sanitize_key($templateSlug),
                'fallback_reason' => $fallbackReason,
            ]);

            return new StructuralContainerResolution([], [], true, true, false, [], $fallbackReason);
        }

        $viewablePostTypes = $this->resolveViewablePostTypes();
        if (empty($viewablePostTypes)) {
            return new StructuralContainerResolution([], [], false, false, false);
        }

        // phpcs:disable WordPress.DB.SlowDBQuery.slow_db_query_meta_key,WordPress.DB.SlowDBQuery.slow_db_query_meta_value -- Event-driven template assignment discovery intentionally queries the core page-template meta and returns IDs only.
        $candidateIds = get_posts([
            'post_type' => $viewablePostTypes,
            'post_status' => 'publish',
            'posts_per_page' => -1,
            'fields' => 'ids',
            'no_found_rows' => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
            'meta_key' => '_wp_page_template',
            'meta_value' => $templateSlug,
        ]);
        // phpcs:enable WordPress.DB.SlowDBQuery.slow_db_query_meta_key,WordPress.DB.SlowDBQuery.slow_db_query_meta_value

        if (!is_array($candidateIds)) {
            return new StructuralContainerResolution([], [], true, true, true);
        }

        $directTargetIds = [];

        foreach ($candidateIds as $candidateId) {
            $candidate = get_post((int) $candidateId);
            if (!$candidate instanceof \WP_Post) {
                continue;
            }

            if (!ContentVisibility::canEmitPreciseTarget($candidate)) {
                continue;
            }

            $directTargetIds[] = (int) $candidate->ID;
        }

        $directTargetIds = ContentVisibility::normalizePostIds($directTargetIds);

        Debug::verbose(sprintf(
            'wp_template provider: post_id=%d template_slug=%s direct=%d',
            $postId,
            sanitize_key($templateSlug),
            count($directTargetIds)
        ));

        if (empty($directTargetIds)) {
            return new StructuralContainerResolution([], [], false, false, false);
        }

        return new StructuralContainerResolution($directTargetIds, [], true, false, false);
    }

    /**
     * Resolve viewable post types for deterministic template assignment scans.
     *
     * @return string[]
     */
    private function resolveViewablePostTypes(): array
    {
        return ContentVisibility::getViewablePublicPostTypes();
    }

    /**
     * Determine whether a database template is a core hierarchy template.
     *
     * @param \WP_Post $post Template post.
     * @return bool
     */
    private function isResolvedHierarchyTemplate(\WP_Post $post): bool
    {
        $templateSlug = sanitize_key((string) $post->post_name);
        if ($templateSlug === 'page') {
            return true;
        }

        if (!function_exists('get_block_template')) {
            return false;
        }

        $themes = get_the_terms((int) $post->ID, 'wp_theme');
        if (!is_array($themes)) {
            return false;
        }

        foreach ($themes as $theme) {
            if (!$theme instanceof \WP_Term) {
                continue;
            }

            $themeSlug = sanitize_key((string) $theme->slug);
            if ($themeSlug === '') {
                continue;
            }

            try {
                $template = get_block_template($themeSlug . '//' . $templateSlug, 'wp_template');
            } catch (\Throwable $throwable) {
                Debug::warn('block-template identity lookup failed', [
                    'post_id' => (int) $post->ID,
                    'template_slug' => $templateSlug,
                    'exception' => $throwable->getMessage(),
                ]);
                return true;
            }

            if (!$template instanceof \WP_Block_Template || absint($template->wp_id ?? 0) !== (int) $post->ID) {
                continue;
            }

            return empty($template->is_custom);
        }

        return false;
    }

    /**
     * Detect supported and explicitly unsupported archive template slugs.
     *
     * @param string $slug Template post_name.
     * @return array<string,mixed>|null
     */
    private function detectArchiveTemplate(string $slug): ?array
    {
        $slug = sanitize_key($slug);
        if ($slug === '') {
            return null;
        }

        if ($slug === 'category') {
            return [
                'supported' => true,
                'kind' => 'taxonomy_archive',
                'taxonomy' => 'category',
            ];
        }

        if ($slug === 'tag') {
            return [
                'supported' => true,
                'kind' => 'taxonomy_archive',
                'taxonomy' => 'post_tag',
            ];
        }

        if (str_starts_with($slug, 'category-') || str_starts_with($slug, 'tag-')) {
            return [
                'supported' => false,
                'kind' => 'taxonomy_archive',
                'fallback_reason' => 'ambiguous_taxonomy_template_slug',
            ];
        }

        if ($slug === 'taxonomy') {
            return [
                'supported' => false,
                'kind' => 'taxonomy_archive',
                'fallback_reason' => 'unsupported_archive_template_slug',
            ];
        }

        if (str_starts_with($slug, 'taxonomy-')) {
            $taxonomy = sanitize_key(substr($slug, strlen('taxonomy-')));
            if ($taxonomy !== '' && taxonomy_exists($taxonomy)) {
                return [
                    'supported' => true,
                    'kind' => 'taxonomy_archive',
                    'taxonomy' => $taxonomy,
                ];
            }

            return [
                'supported' => false,
                'kind' => 'taxonomy_archive',
                'taxonomy' => $taxonomy,
                'fallback_reason' => 'ambiguous_taxonomy_template_slug',
            ];
        }

        if (in_array($slug, ['archive', 'home', 'index', 'author', 'date'], true)) {
            return [
                'supported' => false,
                'kind' => 'generic_archive',
                'fallback_reason' => 'unsupported_archive_template_slug',
            ];
        }

        if (str_starts_with($slug, 'archive-')) {
            $postType = sanitize_key(substr($slug, strlen('archive-')));
            if ($postType !== '' && $postType !== 'post' && post_type_exists($postType)) {
                return [
                    'supported' => true,
                    'kind' => 'post_type_archive',
                    'post_type' => $postType,
                ];
            }

            return [
                'supported' => false,
                'kind' => 'post_type_archive',
                'post_type' => $postType,
                'fallback_reason' => 'unsupported_archive_template_slug',
            ];
        }

        return null;
    }

    /**
     * Detect supported and explicitly unsupported single template slugs.
     *
     * @param string $slug Template post_name.
     * @return array<string,mixed>|null
     */
    private function detectSingleTemplate(string $slug): ?array
    {
        $slug = sanitize_key($slug);
        if ($slug === '') {
            return null;
        }

        if ($slug === 'single') {
            return [
                'supported' => true,
                'kind' => 'single',
                'post_types' => $this->resolveBareSingleTemplatePostTypes(),
            ];
        }

        if (!str_starts_with($slug, 'single-')) {
            return null;
        }

        $resolved = $this->resolveSingleTemplatePostTypeAndSlug(substr($slug, strlen('single-')));
        if (empty($resolved['ok'])) {
            return [
                'supported' => false,
                'kind' => 'single',
                'fallback_reason' => sanitize_key((string) ($resolved['fallback_reason'] ?? 'malformed_single_template_slug')),
            ];
        }

        $postType = sanitize_key((string) ($resolved['post_type'] ?? ''));
        $postSlug = sanitize_title((string) ($resolved['post_slug'] ?? ''));
        if ($postSlug === '') {
            return [
                'supported' => true,
                'kind' => 'post_type_single',
                'post_type' => $postType,
                'post_types' => [$postType],
            ];
        }

        return [
            'supported' => true,
            'kind' => 'specific_single',
            'post_type' => $postType,
            'post_slug' => $postSlug,
        ];
    }

    /**
     * Resolve post types targeted by the bare `single` template.
     *
     * @return string[]
     */
    private function resolveBareSingleTemplatePostTypes(): array
    {
        $viewablePostTypes = ContentVisibility::getViewablePublicPostTypes();
        $defaultPostTypes = array_values(array_diff($viewablePostTypes, ['page']));

        /**
         * Filter post types resolved for the Gutenberg bare `single` template.
         *
         * @param string[] $defaultPostTypes Publicly viewable post types eligible for `single` (excludes `page`; re-add to opt back in).
         */
        $filtered = apply_filters('ekesto_ci_single_template_post_types', $defaultPostTypes);
        $filtered = is_array($filtered) ? $filtered : [];
        $filtered = ContentVisibility::normalizePostTypes($filtered);

        return array_values(array_intersect($filtered, $viewablePostTypes));
    }

    /**
     * Resolve a post type and optional post slug from a single template suffix.
     *
     * @param string $suffix Template slug after `single-`.
     * @return array{ok:bool,post_type:string,post_slug:string,fallback_reason:string}
     */
    private function resolveSingleTemplatePostTypeAndSlug(string $suffix): array
    {
        $suffix = sanitize_key($suffix);
        if ($suffix === '') {
            return [
                'ok' => false,
                'post_type' => '',
                'post_slug' => '',
                'fallback_reason' => 'malformed_single_template_slug',
            ];
        }

        $postTypes = ContentVisibility::getViewablePublicPostTypes();
        usort($postTypes, static fn(string $a, string $b): int => strlen($b) <=> strlen($a));

        foreach ($postTypes as $postType) {
            if ($suffix === $postType) {
                return [
                    'ok' => true,
                    'post_type' => $postType,
                    'post_slug' => '',
                    'fallback_reason' => '',
                ];
            }

            $prefix = $postType . '-';
            if (str_starts_with($suffix, $prefix)) {
                $postSlug = sanitize_title(substr($suffix, strlen($prefix)));
                if ($postSlug === '') {
                    return [
                        'ok' => false,
                        'post_type' => $postType,
                        'post_slug' => '',
                        'fallback_reason' => 'malformed_single_template_slug',
                    ];
                }

                return [
                    'ok' => true,
                    'post_type' => $postType,
                    'post_slug' => $postSlug,
                    'fallback_reason' => '',
                ];
            }
        }

        return [
            'ok' => false,
            'post_type' => '',
            'post_slug' => '',
            'fallback_reason' => 'unknown_single_template_post_type',
        ];
    }

    /**
     * Resolve all published, viewable posts for one or more single template post types.
     *
     * @param string[] $postTypes Post type keys.
     * @return array{ok:bool,post_ids:int[],fallback_reason:string}
     */
    private function resolveSingleTemplateTargets(array $postTypes): array
    {
        $viewablePostTypes = ContentVisibility::getViewablePublicPostTypes();
        $postTypes = ContentVisibility::normalizePostTypes($postTypes);
        $postTypes = array_values(array_intersect($postTypes, $viewablePostTypes));
        if (empty($postTypes)) {
            return [
                'ok' => true,
                'post_ids' => [],
                'fallback_reason' => '',
            ];
        }

        $candidateIds = get_posts([
            'post_type' => $postTypes,
            'post_status' => 'publish',
            'posts_per_page' => -1,
            'fields' => 'ids',
            'no_found_rows' => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
        ]);

        if (!is_array($candidateIds)) {
            return [
                'ok' => false,
                'post_ids' => [],
                'fallback_reason' => 'single_template_query_failed',
            ];
        }

        return [
            'ok' => true,
            'post_ids' => $this->filterViewableCandidateIds($candidateIds),
            'fallback_reason' => '',
        ];
    }

    /**
     * Resolve one specific single template to exactly one published, viewable post.
     *
     * @param string $postType Post type key.
     * @param string $postSlug Post slug.
     * @return array{ok:bool,post_ids:int[],fallback_reason:string}
     */
    private function resolveSpecificSingleTemplateTarget(string $postType, string $postSlug): array
    {
        $postType = sanitize_key($postType);
        $postSlug = sanitize_title($postSlug);
        if (
            $postType === ''
            || $postSlug === ''
            || !in_array($postType, ContentVisibility::getViewablePublicPostTypes(), true)
        ) {
            return [
                'ok' => false,
                'post_ids' => [],
                'fallback_reason' => 'invalid_single_template_extraction',
            ];
        }

        $candidateIds = get_posts([
            'post_type' => $postType,
            'post_status' => 'publish',
            'name' => $postSlug,
            'posts_per_page' => 2,
            'fields' => 'ids',
            'no_found_rows' => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
        ]);

        if (!is_array($candidateIds) || count($candidateIds) !== 1) {
            return [
                'ok' => false,
                'post_ids' => [],
                'fallback_reason' => 'specific_single_template_ambiguous',
            ];
        }

        $targetIds = $this->filterViewableCandidateIds($candidateIds);
        if (count($targetIds) !== 1) {
            return [
                'ok' => false,
                'post_ids' => [],
                'fallback_reason' => 'non_viewable_single_template_target',
            ];
        }

        return [
            'ok' => true,
            'post_ids' => $targetIds,
            'fallback_reason' => '',
        ];
    }

    /**
     * Filter candidate IDs to deterministic precise frontend targets.
     *
     * @param int[] $candidateIds Candidate post IDs.
     * @return int[]
     */
    private function filterViewableCandidateIds(array $candidateIds): array
    {
        $directTargetIds = [];
        foreach ($candidateIds as $candidateId) {
            $candidate = get_post((int) $candidateId);
            if (!$candidate instanceof \WP_Post) {
                continue;
            }

            if (!ContentVisibility::canEmitPreciseTarget($candidate)) {
                continue;
            }

            $directTargetIds[] = (int) $candidate->ID;
        }

        return ContentVisibility::normalizePostIds($directTargetIds);
    }

    /**
     * Resolve all public term archive URLs for a taxonomy archive template.
     *
     * @param string $taxonomy Taxonomy key.
     * @return array{ok:bool,urls:string[],fallback_reason:string,term_count:int}
     */
    private function resolvePublicTaxonomyArchiveUrls(string $taxonomy): array
    {
        $taxonomy = sanitize_key($taxonomy);
        $taxonomyObject = get_taxonomy($taxonomy);
        if (
            !$taxonomyObject instanceof \WP_Taxonomy
            || empty($taxonomyObject->public)
            || empty($taxonomyObject->publicly_queryable)
        ) {
            return [
                'ok' => false,
                'urls' => [],
                'fallback_reason' => 'private_or_non_queryable_taxonomy',
                'term_count' => 0,
            ];
        }

        $termIds = get_terms([
            'taxonomy' => $taxonomy,
            'fields' => 'ids',
            'hide_empty' => false,
            'update_term_meta_cache' => false,
        ]);

        if (is_wp_error($termIds) || !is_array($termIds)) {
            return [
                'ok' => false,
                'urls' => [],
                'fallback_reason' => 'term_query_failed',
                'term_count' => 0,
            ];
        }

        $termCount = count($termIds);
        $urls = [];

        foreach ($termIds as $termId) {
            $termId = absint($termId);
            if ($termId <= 0) {
                continue;
            }

            $url = get_term_link($termId, $taxonomy);
            if (is_wp_error($url) || !is_string($url) || trim($url) === '') {
                return [
                    'ok' => false,
                    'urls' => [],
                    'fallback_reason' => 'term_link_resolution_failed',
                    'term_count' => $termCount,
                ];
            }

            $urls[] = trim($url);
        }

        return [
            'ok' => true,
            'urls' => ContentVisibility::normalizeUrls($urls),
            'fallback_reason' => '',
            'term_count' => $termCount,
        ];
    }

    /**
     * Resolve the public archive URL for one custom post type.
     *
     * @param string $postType Post type key.
     * @return array{ok:bool,urls:string[],fallback_reason:string,term_count:int}
     */
    private function resolvePostTypeArchiveUrl(string $postType): array
    {
        $postType = sanitize_key($postType);
        $postTypeObject = get_post_type_object($postType);
        if (
            !$postTypeObject instanceof \WP_Post_Type
            || empty($postTypeObject->public)
            || empty($postTypeObject->publicly_queryable)
            || empty($postTypeObject->has_archive)
        ) {
            return [
                'ok' => false,
                'urls' => [],
                'fallback_reason' => 'post_type_has_no_public_archive',
                'term_count' => 0,
            ];
        }

        $url = get_post_type_archive_link($postType);
        if (!is_string($url) || trim($url) === '') {
            return [
                'ok' => false,
                'urls' => [],
                'fallback_reason' => 'post_type_archive_link_failed',
                'term_count' => 0,
            ];
        }

        return [
            'ok' => true,
            'urls' => ContentVisibility::normalizeUrls([$url]),
            'fallback_reason' => '',
            'term_count' => 0,
        ];
    }
}
