<?php
/**
 * Discovers Bricks dynamic data tags at runtime.
 *
 * @package LolaCoreForBricks
 */

declare(strict_types=1);

namespace LolaCoreForBricks\Support;

/**
 * Tags come from Bricks providers (WP, ACF, Woo, …), never from a hardcoded list.
 */
final class DynamicDataCatalog
{
    /**
     * @return array<int, array{name: string, label: string, group: string, provider: string}>
     */
    public function list(): array
    {
        $tags = [];

        if (class_exists(\Bricks\Integrations\Dynamic_Data\Providers::class)) {
            $registered = apply_filters('bricks/dynamic_tags_list', []);
            if (is_array($registered)) {
                foreach ($registered as $tag) {
                    $normalized = $this->normalizeTag($tag);
                    if ($normalized !== null) {
                        $tags[] = $normalized;
                    }
                }
            }
        }

        $extra = apply_filters('lolacore_bricks_dynamic_tags', []);
        if (is_array($extra)) {
            foreach ($extra as $tag) {
                $normalized = $this->normalizeTag($tag);
                if ($normalized !== null) {
                    $tags[] = $normalized;
                }
            }
        }

        return $this->dedupe($tags);
    }

    /**
     * @return array<string, array<int, array{name: string, label: string, group: string, provider: string}>>
     */
    public function grouped(): array
    {
        $groups = [];
        foreach ($this->list() as $tag) {
            $provider = $tag['provider'] !== '' ? $tag['provider'] : $tag['group'];
            $groups[$provider][] = $tag;
        }

        ksort($groups);

        return $groups;
    }

    public function exists(string $tag): bool
    {
        $needle = $this->canonical($tag);
        if ($needle === '') {
            return false;
        }

        foreach ($this->list() as $item) {
            if ($this->canonical($item['name']) === $needle) {
                return true;
            }
        }

        return false;
    }

    public function wrap(string $tag): string
    {
        $name = $this->canonical($tag);

        return $name === '' ? '' : '{' . $name . '}';
    }

    public function canonical(string $tag): string
    {
        $tag = trim($tag);
        if ($tag === '') {
            return '';
        }

        if (str_starts_with($tag, '{') && str_ends_with($tag, '}')) {
            $tag = substr($tag, 1, -1);
        }

        $tag = trim($tag);

        return preg_match('/^[a-zA-Z0-9_:-]+$/', $tag) === 1 ? $tag : '';
    }

    /**
     * @param mixed $tag
     * @return array{name: string, label: string, group: string, provider: string}|null
     */
    private function normalizeTag(mixed $tag): ?array
    {
        if (is_string($tag) && $tag !== '') {
            $name = $this->wrap($tag);

            return $name === '' ? null : [
                'name'     => $name,
                'label'    => $this->canonical($tag),
                'group'    => 'other',
                'provider' => '',
            ];
        }

        if (!is_array($tag)) {
            return null;
        }

        $name = (string) ($tag['name'] ?? $tag['tag'] ?? '');
        $name = $this->wrap($name);
        if ($name === '') {
            return null;
        }

        return [
            'name'     => $name,
            'label'    => (string) ($tag['label'] ?? $this->canonical($name)),
            'group'    => (string) ($tag['group'] ?? 'other'),
            'provider' => (string) ($tag['provider'] ?? ''),
        ];
    }

    /**
     * @param array<int, array{name: string, label: string, group: string, provider: string}> $tags
     * @return array<int, array{name: string, label: string, group: string, provider: string}>
     */
    private function dedupe(array $tags): array
    {
        $unique = [];
        foreach ($tags as $tag) {
            $unique[$tag['name']] = $tag;
        }

        return array_values($unique);
    }
}
