<?php

namespace App\Support\Traits;

use DOMElement;

/*
 * Трейт: низкоуровневые хелперы BlockLayoutParser —
 * извлечение полей, токенизация DOM, сериализация.
 * Используется классом BlockLayoutParser.
 */
trait BlockLayoutHelpersTrait
{
    // ── Извлечение полей из DOM-элемента ──────────────────────────────────

    /** Рекурсивно собирает поля из $el в массив $raw. */
    private function extractFields(DOMElement $el, array &$raw, string $var): void
    {
        $tag = strtolower($el->tagName);

        // Заголовки → text
        if (in_array($tag, ['h1','h2','h3','h4','h5','h6'])) {
            $text = trim($el->textContent);
            if ($text !== '') {
                $raw[] = $this->mkField('heading', 'Заголовок', 'text', $el, null, $var);
            }
            return;
        }

        // Абзац → textarea
        if ($tag === 'p') {
            $text = trim($el->textContent);
            if ($text !== '') {
                $raw[] = $this->mkField('text', 'Текст', 'textarea', $el, null, $var);
            }
            return;
        }

        // Картинка → image
        if ($tag === 'img') {
            if ($el->getAttribute('src') !== '') {
                $raw[] = $this->mkField('image', 'Изображение', 'image', $el, 'src', $var);
            }
            return;
        }

        // Ссылка → url + text
        if ($tag === 'a') {
            $href = $el->getAttribute('href');
            $text = trim($el->textContent);
            if ($text !== '') {
                $raw[] = $this->mkField('link_text', 'Текст ссылки', 'text', $el, null, $var);
            }
            if ($href !== '' && $href !== '#') {
                $raw[] = $this->mkField('link_url', 'URL ссылки', 'url', $el, 'href', $var);
            }
            return;
        }

        // Листовой элемент с коротким текстом → text
        $childEls = $this->elementChildren($el);
        if (empty($childEls)) {
            $text = trim($el->textContent);
            if ($text !== '' && mb_strlen($text) <= 300) {
                $raw[] = $this->mkField('label', 'Подпись', 'text', $el, null, $var);
            }
            return;
        }

        // Иначе рекурсируем
        foreach ($childEls as $child) {
            $this->extractFields($child, $raw, $var);
        }
    }

    // ── Создание поля ─────────────────────────────────────────────────────

    private function mkField(string $base, string $label, string $type, DOMElement $el, ?string $attr, string $var): array
    {
        $name = $base;
        $i    = 2;
        while (isset($this->usedNames[$name])) {
            $name = $base . '_' . $i++;
        }
        $this->usedNames[$name] = true;

        return [
            'name'   => $name,
            'label'  => $label,
            'type'   => $type,
            '_el'    => $el,
            '_attr'  => $attr,
            '_var'   => $var,
        ];
    }

    // ── Токенизация DOM ───────────────────────────────────────────────────

    /** Заменяет содержимое/атрибуты в DOM на уникальные токены. */
    private function applyTokens(array $raw): void
    {
        foreach ($raw as $field) {
            $token = 'BLTKN' . strtoupper($field['name']) . 'END';
            $expr  = "{{ {$field['_var']}['{$field['name']}'] }}";
            $this->tokens[$token] = $expr;

            /** @var DOMElement $el */
            $el = $field['_el'];

            if ($field['_attr'] !== null) {
                $el->setAttribute($field['_attr'], $token);
            } else {
                while ($el->firstChild) {
                    $el->removeChild($el->firstChild);
                }
                $el->appendChild($this->dom->createTextNode($token));
            }
        }
    }

    private function replaceTokens(string $html): string
    {
        foreach ($this->tokens as $token => $expr) {
            $html = str_replace($token, $expr, $html);
        }
        return $html;
    }

    // ── Сериализация ──────────────────────────────────────────────────────

    private function serializeChildren(DOMElement $el): string
    {
        $html = '';
        foreach ($el->childNodes as $child) {
            $html .= $this->dom->saveHTML($child);
        }
        return $html;
    }

    // ── Хелперы ───────────────────────────────────────────────────────────

    /** Возвращает только публичные поля (без служебных _el, _attr, _var). */
    private function publicFields(array $raw): array
    {
        return array_values(array_map(fn($f) => array_filter(
            $f,
            fn($k) => !str_starts_with($k, '_'),
            ARRAY_FILTER_USE_KEY
        ), $raw));
    }

    /** Возвращает дочерние DOM-элементы (без текстовых узлов). */
    private function elementChildren(DOMElement $el): array
    {
        $result = [];
        foreach ($el->childNodes as $node) {
            if ($node->nodeType === XML_ELEMENT_NODE) {
                $result[] = $node;
            }
        }
        return $result;
    }
}
