<?php

declare(strict_types=1);

/*
 * Chill is a software for social workers
 *
 * For the full copyright and license information, please view
 * the LICENSE file that was distributed with this source code.
 */

namespace Chill\DocGeneratorBundle\Serializer\Encoder;

use Symfony\Component\Serializer\Encoder\EncoderInterface;
use Symfony\Component\Serializer\Exception\UnexpectedValueException;

class DocGenEncoder implements EncoderInterface
{
    public function encode($data, $format, array $context = [])
    {
        if (!$this->isAssociative($data)) {
            throw new UnexpectedValueException('Only associative arrays are allowed; lists are not allowed');
        }

        $result = [];
        $this->recusiveEncoding($data, $result, '');

        return $result;
    }

    public function supportsEncoding($format)
    {
        return 'docgen' === $format;
    }

    private function canonicalizeKey(string $path, string $key): string
    {
        return '' === $path ? $key : $path.'_'.$key;
    }

    private function isAssociative(array $data)
    {
        $keys = \array_keys($data);

        return \array_keys($keys) !== $keys;
    }

    private function recusiveEncoding(array $data, array &$result, $path)
    {
        if ($this->isAssociative($data)) {
            foreach ($data as $key => $value) {
                if (\is_array($value)) {
                    $this->recusiveEncoding($value, $result, $this->canonicalizeKey($path, $key));
                } else {
                    $result[$this->canonicalizeKey($path, $key)] = $value;
                }
            }
        } else {
            foreach ($data as $elem) {
                if (!$this->isAssociative($elem)) {
                    throw new UnexpectedValueException(sprintf('Embedded loops are not allowed. See data under %s path', $path));
                }

                $sub = [];
                $this->recusiveEncoding($elem, $sub, '');
                $result[$path][] = $sub;
            }
        }
    }
}
