<?php

if (! defined('ABSPATH')) {
    exit;
} final class PufferGo_Block_Translatable
{
    public const SKIP_BLOCKS = array('core/code', 'core/html', 'core/shortcode', 'core/freeform', );

    public const CONFIG_TEXT_TYPES = array('text', 'textarea', 'richtext');

    private $blocks;

    public function __construct(array $blocks)
    {
        $this->blocks = $blocks;
    }

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

        foreach ($this->blocks as $i => $block) {
            if (is_array($block)) {
                $this->collect_from_block($block, $prefix . 'b' . $i, $items);
            }
        }

return $items;
    }

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

    public function apply(array $translations, string $prefix = ''): string
    {
        $out = '';

        foreach ($this->blocks as $i => $block) {
            if (is_array($block)) {
                $out .= $this->serialize_block($block, $translations, $prefix . 'b' . $i);
            }
        }

return $out;
    }

    private function collect_from_block(array $block, string $path, array &$items): void
    {
        $name = isset($block['blockName']) ? $block['blockName'] : null;

        if (null !== $name && PufferGo_Tailwind_Block::is_tailwind_block($block)) {
            $attrs = (isset($block['attrs']) && is_array($block['attrs'])) ? $block['attrs'] : array();

            if (self::is_config_block($attrs) && class_exists('PufferGo_Alpine_SSR')) {
                $template = PufferGo_Alpine_SSR::normalize_shorthands((string) $attrs['configTemplate']);
                $tpl_frag = new PufferGo_HTML_Translatable($template);
                $items = array_merge($items, $tpl_frag->items($path . 'tpl'));
                $items = array_merge($items, self::collect_config_items($attrs['configSchema'], $attrs['configData'], $path . 'cfg_'));

                return;
            } $content = isset($attrs['content']) ? (string) $attrs['content'] : '';

            if ('' !== $content) {
                $fragment = new PufferGo_HTML_Translatable($content);
                $items = array_merge($items, $fragment->items($path . 'p'));
            }

return;
        } $inner_blocks = (isset($block['innerBlocks']) && is_array($block['innerBlocks'])) ? $block['innerBlocks'] : array();

        if ($this->translates_own_text($name, $inner_blocks)) {
            $inner_content = (isset($block['innerContent']) && is_array($block['innerContent'])) ? $block['innerContent'] : array();

            foreach ($inner_content as $c => $chunk) {
                if (is_string($chunk) && '' !== trim($chunk)) {
                    $fragment = new PufferGo_HTML_Translatable($chunk);
                    $items = array_merge($items, $fragment->items($path . 'c' . $c . 'p'));
                }
            }
        }

foreach ($inner_blocks as $j => $child) {
            if (is_array($child)) {
                $this->collect_from_block($child, $path . '_' . $j, $items);
            }
        }
    }

    private function serialize_block(array $block, array $translations, string $path): string
    {
        $name = isset($block['blockName']) ? $block['blockName'] : null;

        if (null !== $name && PufferGo_Tailwind_Block::is_tailwind_block($block)) {
            $attrs = (isset($block['attrs']) && is_array($block['attrs'])) ? $block['attrs'] : array();

            if (self::is_config_block($attrs) && class_exists('PufferGo_Alpine_SSR')) {
                $template = PufferGo_Alpine_SSR::normalize_shorthands((string) $attrs['configTemplate']);
                $tpl_frag = new PufferGo_HTML_Translatable($template);
                $tpl_translated = $tpl_frag->apply($translations, $path . 'tpl');
                $config = self::apply_config($attrs['configSchema'], $attrs['configData'], $translations, $path . 'cfg_');
                $bs_raw = isset($attrs['blockStyle']) && is_array($attrs['blockStyle']) ? $attrs['blockStyle'] : array();
                $html = PufferGo_Alpine_SSR::inject_config_data($tpl_translated, $config, $bs_raw);
                $attrs['configTemplate'] = $tpl_translated;
                $attrs['configData'] = $config;
                $attrs['content'] = $html;
                $scope = isset($attrs['scopeId']) ? (string) $attrs['scopeId'] : '';
                $inner = '<div class="wp-block-puffergo-tailwind-container ' . esc_attr($scope) . '">' . $html . '</div>';

                return get_comment_delimited_block_content(PufferGo_Tailwind_Block::BLOCK_NAME, $attrs, $inner);
            } $content = isset($attrs['content']) ? (string) $attrs['content'] : '';
            $html = $content;

            if ('' !== $content) {
                $fragment = new PufferGo_HTML_Translatable($content);
                $html = $fragment->apply($translations, $path . 'p');
            } $attrs['content'] = $html;
            $scope = isset($attrs['scopeId']) ? (string) $attrs['scopeId'] : '';
            $inner = '<div class="wp-block-puffergo-tailwind-container ' . esc_attr($scope) . '">' . $html . '</div>';

            return get_comment_delimited_block_content(PufferGo_Tailwind_Block::BLOCK_NAME, $attrs, $inner);
        } $inner_blocks = (isset($block['innerBlocks']) && is_array($block['innerBlocks'])) ? $block['innerBlocks'] : array();
        $inner_content = (isset($block['innerContent']) && is_array($block['innerContent'])) ? $block['innerContent'] : array();
        $translate_self = $this->translates_own_text($name, $inner_blocks);
        $content = '';
        $child_index = 0;

        foreach ($inner_content as $c => $chunk) {
            if (is_string($chunk)) {
                if ($translate_self && '' !== trim($chunk)) {
                    $fragment = new PufferGo_HTML_Translatable($chunk);
                    $content .= $fragment->apply($translations, $path . 'c' . $c . 'p');
                } else {
                    $content .= $chunk;
                }
            } else {
                $child = isset($inner_blocks[ $child_index ]) ? $inner_blocks[ $child_index ] : null;

                if (is_array($child)) {
                    $content .= $this->serialize_block($child, $translations, $path . '_' . $child_index);
                } ++$child_index;
            }
        }

if (null === $name) {
            return $content;
        } $attrs = (isset($block['attrs']) && is_array($block['attrs'])) ? $block['attrs'] : array();

        return get_comment_delimited_block_content($name, $attrs, $content);
    }

    private function translates_own_text(?string $name, array $inner_blocks): bool
    {
        if (! empty($inner_blocks)) {
            return false;
        }

if (null === $name) {
            return true;
        }

return ! in_array($name, self::SKIP_BLOCKS, true);
    }

    private static function is_config_block(array $attrs): bool
    {
        return ! empty($attrs['configTemplate']) && isset($attrs['configData']) && is_array($attrs['configData']) && isset($attrs['configSchema']) && is_array($attrs['configSchema']);
    }

    private static function collect_config_items(array $schema, array $data, string $prefix): array
    {
        $items = array();

        foreach ($schema as $key => $field) {
            if (! is_array($field)) {
                continue;
            } $type = isset($field['type']) ? (string) $field['type'] : '';

            if ('array' === $type) {
                $item_schema = (isset($field['itemSchema']) && is_array($field['itemSchema'])) ? $field['itemSchema'] : array();
                $rows = (isset($data[ $key ]) && is_array($data[ $key ])) ? $data[ $key ] : array();

                foreach ($rows as $index => $row) {
                    if (is_array($row)) {
                        $items = array_merge($items, self::collect_config_items($item_schema, $row, $prefix . $key . '_' . $index . '_'));
                    }
                } continue;
            }

if (! in_array($type, self::CONFIG_TEXT_TYPES, true)) {
                continue;
            } $value = isset($data[ $key ]) ? $data[ $key ] : '';

            if (is_string($value) && self::is_translatable_text($value)) {
                $items[] = array('id' => $prefix . $key, 'text' => $value, 'contentType' => 'plain', );
            }
        }

return $items;
    }

    private static function apply_config(array $schema, array $data, array $translations, string $prefix): array
    {
        foreach ($schema as $key => $field) {
            if (! is_array($field)) {
                continue;
            } $type = isset($field['type']) ? (string) $field['type'] : '';

            if ('array' === $type) {
                $item_schema = (isset($field['itemSchema']) && is_array($field['itemSchema'])) ? $field['itemSchema'] : array();

                if (isset($data[ $key ]) && is_array($data[ $key ])) {
                    foreach ($data[ $key ] as $index => $row) {
                        if (is_array($row)) {
                            $data[ $key ][ $index ] = self::apply_config($item_schema, $row, $translations, $prefix . $key . '_' . $index . '_');
                        }
                    }
                } continue;
            }

if (! in_array($type, self::CONFIG_TEXT_TYPES, true)) {
                continue;
            } $id = $prefix . $key;

            if (isset($translations[ $id ]) && '' !== (string) $translations[ $id ]) {
                $data[ $key ] = (string) $translations[ $id ];
            }
        }

return $data;
    }

    private static function is_translatable_text(string $value): bool
    {
        return '' !== trim($value) && (bool) preg_match('/\p{L}/u', $value);
    }
}
