<?php

if (! defined('ABSPATH')) {
    exit;
} final class PufferGo_Meta_Translatable
{
    private $source;

    private $specs;

    public function __construct(WP_Post $source, array $specs)
    {
        $this->source = $source;
        $this->specs = $specs;
    }

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

        foreach ($this->specs as $meta_key => $spec) {
            $raw = get_post_meta($this->source->ID, $meta_key, true);

            if (true === $spec) {
                if (is_string($raw) && '' !== trim($raw)) {
                    $items[] = array('id' => $this->item_id($prefix, $meta_key, ''), 'text' => $raw, 'contentType' => 'plain', );
                } continue;
            }

if (! is_array($spec) || ! is_string($raw) || '' === $raw) {
                continue;
            } $data = json_decode($raw, true);

            if (! is_array($data)) {
                continue;
            }

foreach ($this->leaves($data, $spec) as $path => $value) {
                $items[] = array('id' => $this->item_id($prefix, $meta_key, $path), 'text' => $value, 'contentType' => 'plain', );
            }
        }

return $items;
    }

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

        foreach ($this->specs as $meta_key => $spec) {
            $raw = get_post_meta($this->source->ID, $meta_key, true);

            if (true === $spec) {
                $id = $this->item_id($prefix, $meta_key, '');

                if (isset($translations[ $id ]) && '' !== $translations[ $id ]) {
                    $out[ $meta_key ] = (string) $translations[ $id ];
                } continue;
            }

if (! is_array($spec) || ! is_string($raw) || '' === $raw) {
                continue;
            } $data = json_decode($raw, true);

            if (! is_array($data)) {
                continue;
            } $changed = false;

            foreach (array_keys($this->leaves($data, $spec)) as $path) {
                $id = $this->item_id($prefix, $meta_key, $path);

                if (isset($translations[ $id ]) && '' !== $translations[ $id ]) {
                    $this->set_leaf($data, explode('.', $path), (string) $translations[ $id ]);
                    $changed = true;
                }
            }

if ($changed) {
                $out[ $meta_key ] = (string) wp_json_encode($data);
            }
        }

return $out;
    }

    private function leaves(array $data, array $paths): array
    {
        $out = array();

        foreach ($paths as $pattern) {
            $this->collect_path($data, explode('.', $pattern), array(), $out);
        }

return $out;
    }

    private function collect_path($node, array $segments, array $trail, array &$out): void
    {
        if (empty($segments)) {
            if (is_string($node) && '' !== trim($node)) {
                $out[ implode('.', $trail) ] = $node;
            }

return;
        } $segment = $segments[0];
        $rest = array_slice($segments, 1);

        if ('*' === $segment) {
            if (! is_array($node)) {
                return;
            }

foreach ($node as $index => $child) {
                $this->collect_path($child, $rest, array_merge($trail, array((string) $index)), $out);
            }

return;
        }

if (is_array($node) && array_key_exists($segment, $node)) {
            $this->collect_path($node[ $segment ], $rest, array_merge($trail, array($segment)), $out);
        }
    }

    private function set_leaf(array &$data, array $segments, string $value): void
    {
        $segment = $segments[0];

        if (! array_key_exists($segment, $data)) {
            return;
        }

if (count($segments) === 1) {
            if (is_string($data[ $segment ])) {
                $data[ $segment ] = $value;
            }

return;
        }

if (is_array($data[ $segment ])) {
            $this->set_leaf($data[ $segment ], array_slice($segments, 1), $value);
        }
    }

    private function item_id(string $prefix, string $meta_key, string $path): string
    {
        return $prefix . 'meta_' . $meta_key . ('' !== $path ? '__' . $path : '');
    }
}
