<?php
declare(strict_types=1);

namespace Ekesto\CacheInvalidator\Integrations\Structural\Core;

use Ekesto\CacheInvalidator\Core\Elementor\ElementorIntegrationRegistry;
use Ekesto\CacheInvalidator\Integrations\Structural\Providers\Contracts\StructuralContainerProviderInterface;
use Ekesto\CacheInvalidator\Integrations\Structural\Providers\WordPress\NavigationProvider;
use Ekesto\CacheInvalidator\Integrations\Structural\Providers\WordPress\SyncedPatternProvider;
use Ekesto\CacheInvalidator\Integrations\Structural\Providers\WordPress\TemplatePartProvider;
use Ekesto\CacheInvalidator\Integrations\Structural\Providers\WordPress\TemplateProvider;

/**
 * Internal registry for structural container providers.
 */
final class StructuralContainerRegistry
{
    /** @var StructuralContainerProviderInterface[]|null */
    private ?array $resolvedProviders = null;

    /**
     * Return registered structural providers.
     *
     * @return StructuralContainerProviderInterface[]
     */
    public function providers(): array
    {
        if ($this->resolvedProviders !== null) {
            return $this->resolvedProviders;
        }

        $providers = [
            new SyncedPatternProvider(),
            new TemplatePartProvider(),
            new TemplateProvider(),
            new NavigationProvider(),
        ];

        $providers = array_merge($providers, ElementorIntegrationRegistry::current()->structuralProviders());

        $this->resolvedProviders = array_values(array_filter(
            $providers,
            static fn($provider): bool => $provider instanceof StructuralContainerProviderInterface
        ));

        return $this->resolvedProviders;
    }

    /**
     * Return structural post types declared by the registry.
     *
     * @return string[]
     */
    public function structuralPostTypes(): array
    {
        $postTypes = [];

        foreach ($this->providers() as $provider) {
            $postTypes = array_merge($postTypes, $provider->structuralPostTypes());
        }

        $postTypes = array_values(array_unique(array_filter(array_map(
            static fn($postType): string => sanitize_key((string) $postType),
            $postTypes
        ))));
        sort($postTypes, SORT_STRING);

        return $postTypes;
    }

    /**
     * Determine whether the post type is structural.
     *
     * @param string $postType Post type key.
     * @return bool
     */
    public function isStructuralPostType(string $postType): bool
    {
        return in_array(sanitize_key($postType), $this->structuralPostTypes(), true);
    }

    /**
     * Resolve the owning provider for one post object.
     *
     * @param \WP_Post $post Post object.
     * @return StructuralContainerProviderInterface|null
     */
    public function providerForPost(\WP_Post $post): ?StructuralContainerProviderInterface
    {
        foreach ($this->providers() as $provider) {
            if ($provider->supports($post)) {
                return $provider;
            }
        }

        return null;
    }
}
