<?php

declare(strict_types=1);

namespace CpCreation\VitiCore\PageBuilder\Document;

final class BuilderDocumentManager
{
    /**
     * @param list<array<string, mixed>> $document
     *
     * @return list<array<string, mixed>>
     */
    public function normalizeDocument(array $document): array
    {
        return $this->normalizeNodes($document, 'root');
    }

    /**
     * @return array<string, mixed>
     */
    public function inspectorSchema(): array
    {
        return [
            'tagOptions' => [
                'section',
                'div',
                'article',
                'header',
                'footer',
                'nav',
                'main',
                'aside',
                'figure',
                'figcaption',
                'picture',
                'h1',
                'h2',
                'h3',
                'h4',
                'h5',
                'h6',
                'p',
                'span',
                'strong',
                'em',
                'a',
                'button',
                'img',
                'ul',
                'ol',
                'li',
            ],
            'attributes' => [
                ['name' => 'class', 'label' => 'Classes', 'type' => 'text'],
                ['name' => 'id', 'label' => 'ID', 'type' => 'text'],
                ['name' => 'href', 'label' => 'Href', 'type' => 'text'],
                ['name' => 'src', 'label' => 'Src', 'type' => 'text'],
                ['name' => 'alt', 'label' => 'Alt', 'type' => 'text'],
                ['name' => 'title', 'label' => 'Title', 'type' => 'text'],
                ['name' => 'x-data', 'label' => 'x-data', 'type' => 'textarea', 'rows' => 3],
                ['name' => 'x-show', 'label' => 'x-show', 'type' => 'textarea', 'rows' => 2],
                ['name' => '@click', 'label' => '@click', 'type' => 'textarea', 'rows' => 2],
                ['name' => ':class', 'label' => ':class', 'type' => 'textarea', 'rows' => 3],
            ],
            'advanced' => [
                'label' => 'Un attribut par ligne (`nom=valeur`)',
                'placeholder' => 'x-transition.opacity.duration.300ms=true',
                'rows' => 8,
            ],
        ];
    }

    /**
     * @param list<array<string, mixed>> $nodes
     *
     * @return list<array<string, mixed>>
     */
    private function normalizeNodes(array $nodes, string $prefix): array
    {
        $normalized = [];

        foreach ($nodes as $index => $node) {
            if (!\is_array($node)) {
                continue;
            }

            $item = $this->normalizeNode($node, sprintf('%s-%d', $prefix, $index));
            if (null !== $item) {
                $normalized[] = $item;
            }
        }

        return $normalized;
    }

    /**
     * @param array<string, mixed> $node
     *
     * @return array<string, mixed>|null
     */
    private function normalizeNode(array $node, string $nodeId): ?array
    {
        if ('textnode' === ($node['type'] ?? null)) {
            $content = (string) ($node['content'] ?? '');
            if ('' === trim($content)) {
                return null;
            }

            return [
                'type' => 'textnode',
                'content' => $content,
                'builderMeta' => [
                    'nodeId' => $this->normalizedNodeId($node['builderMeta']['nodeId'] ?? null, $nodeId),
                    'editableText' => true,
                ],
            ];
        }

        $tagName = strtolower(trim((string) ($node['tagName'] ?? 'div')));
        if (1 !== preg_match('/^[a-z][a-z0-9:-]*$/i', $tagName)) {
            $tagName = 'div';
        }

        $children = $this->normalizeNodes(
            \is_array($node['components'] ?? null) ? array_values($node['components']) : [],
            $nodeId
        );

        return [
            'type' => 'default',
            'tagName' => $tagName,
            'attributes' => $this->normalizeAttributes(\is_array($node['attributes'] ?? null) ? $node['attributes'] : []),
            'components' => $children,
            'builderMeta' => [
                'nodeId' => $this->normalizedNodeId($node['builderMeta']['nodeId'] ?? null, $nodeId),
                'editableText' => [] === $children || $this->childrenAreTextNodes($children),
            ],
        ];
    }

    /**
     * @param list<array<string, mixed>> $children
     */
    private function childrenAreTextNodes(array $children): bool
    {
        foreach ($children as $child) {
            if (($child['type'] ?? null) !== 'textnode') {
                return false;
            }
        }

        return true;
    }

    /**
     * @param array<string, mixed> $attributes
     *
     * @return array<string, string>
     */
    private function normalizeAttributes(array $attributes): array
    {
        $normalized = [];

        foreach ($attributes as $name => $value) {
            $name = trim((string) $name);
            $value = (string) $value;

            if ('' === $name || '' === trim($value)) {
                continue;
            }

            if (1 !== preg_match('/^[@:a-zA-Z_][@:a-zA-Z0-9_.:-]*$/', $name)) {
                continue;
            }

            if (
                str_starts_with($name, 'data-gjs-')
                || str_starts_with($name, 'data-highlightable')
                || str_starts_with($name, 'data-viti-builder')
                || 'data-viti-field' === $name
            ) {
                continue;
            }

            if (\in_array($name, ['contenteditable', 'draggable', 'spellcheck'], true)) {
                continue;
            }

            $normalized[$name] = $value;
        }

        ksort($normalized, SORT_STRING);

        return $normalized;
    }

    private function normalizedNodeId(mixed $value, string $fallback): string
    {
        $nodeId = trim((string) $value);

        return '' === $nodeId ? $fallback : $nodeId;
    }
}
