<?php

if (! defined('ABSPATH')) {
    exit;
} final class PufferGo_HTML_Translatable
{
    public const TRANSLATABLE_ATTRIBUTES = array('alt', 'title', 'placeholder', 'aria-label');

    public const SKIP_PARENT_TAGS = array('script', 'style');

    private $dom;

    private $body;

    private $targets = array();

    public function __construct(string $html)
    {
        $this->dom = new DOMDocument();
        $previous = libxml_use_internal_errors(true);
        $wrapped = '<!DOCTYPE html><html><head><meta charset="UTF-8"></head><body>' . $html . '</body></html>';
        $this->dom->loadHTML($wrapped, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
        libxml_clear_errors();
        libxml_use_internal_errors($previous);
        $this->body = $this->dom->getElementsByTagName('body')->item(0);

        if ($this->body instanceof DOMElement) {
            $this->collect($this->body);
        }
    }

    private function collect(DOMNode $node): void
    {
        if ($node instanceof DOMElement) {
            foreach (self::TRANSLATABLE_ATTRIBUTES as $attr) {
                if ($node->hasAttribute($attr) && self::is_translatable($node->getAttribute($attr))) {
                    $this->targets[] = array('kind' => 'attr', 'node' => $node, 'name' => $attr, );
                }
            }
        }

foreach (iterator_to_array($node->childNodes) as $child) {
            if ($child instanceof DOMText) {
                $parent_tag = ($child->parentNode instanceof DOMElement) ? strtolower($child->parentNode->nodeName) : '';

                if (in_array($parent_tag, self::SKIP_PARENT_TAGS, true)) {
                    continue;
                }

if (self::is_translatable($child->nodeValue)) {
                    $this->targets[] = array('kind' => 'text', 'node' => $child, );
                }
            } elseif ($child instanceof DOMElement) {
                $this->collect($child);
            }
        }
    }

    private static function is_translatable(string $value): bool
    {
        if ('' === trim($value)) {
            return false;
        }

return (bool) preg_match('/\p{L}/u', $value);
    }

    public function is_empty(): bool
    {
        return empty($this->targets);
    }

    public function items(string $prefix = ''): array
    {
        $items = array();

        foreach ($this->targets as $index => $target) {
            $raw = 'attr' === $target['kind'] ? $target['node']->getAttribute($target['name']) : (string) $target['node']->nodeValue;
            $items[] = array('id' => $prefix . $index, 'text' => self::normalize_whitespace($raw), 'contentType' => 'plain', );
        }

return $items;
    }

    private static function normalize_whitespace(string $value): string
    {
        return trim((string) preg_replace('/\s+/u', ' ', $value));
    }

    public function apply(array $translations, string $prefix = ''): string
    {
        foreach ($this->targets as $index => $target) {
            $id = $prefix . $index;

            if (! array_key_exists($id, $translations)) {
                continue;
            } $value = (string) $translations[ $id ];

            if ('attr' === $target['kind']) {
                $target['node']->setAttribute($target['name'], $value);
            } else {
                $original = (string) $target['node']->nodeValue;

                if ('' !== $value) {
                    $lead = (bool) preg_match('/^\s/u', $original);
                    $trail = (bool) preg_match('/\s$/u', $original);
                    $value = ($lead ? ' ' : '') . $value . ($trail ? ' ' : '');
                } $target['node']->nodeValue = $value;
            }
        }

return $this->to_html();
    }

    public function to_html(): string
    {
        if (! $this->body instanceof DOMElement) {
            return '';
        } $html = '';

        foreach ($this->body->childNodes as $child) {
            $html .= $this->dom->saveHTML($child);
        }

return $html;
    }
}
