<?php

declare(strict_types=1);

final class MyronSiteSnapshotEnvelope
{
    public const SNAPSHOT_VERSION = '1.0';

    public const REQUIRED_DATA_SECTIONS = [
        'siteSettings',
        'designDefinition',
        'customSectionTemplates',
        'pageTemplates',
        'pages',
        'navigation',
    ];

    /**
     * @param array<string, mixed> $envelope
     */
    public static function hash(array $envelope): string
    {
        unset($envelope['sha256']);
        return hash('sha256', self::canonicalJson($envelope));
    }

    /**
     * @param array<string, mixed> $envelope
     * @return array<string, mixed>
     */
    public static function withHash(array $envelope): array
    {
        unset($envelope['sha256']);
        $envelope['sha256'] = self::hash($envelope);
        return $envelope;
    }

    /**
     * @param array<string, mixed> $payload
     */
    public static function canonicalJson(array $payload): string
    {
        self::sortRecursive($payload);
        return json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }

    /**
     * @param array<string, mixed> $payload
     */
    public static function prettyJson(array $payload): string
    {
        self::sortRecursive($payload);
        return json_encode($payload, JSON_THROW_ON_ERROR | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "\n";
    }

    private static function sortRecursive(mixed &$value): void
    {
        if (!is_array($value)) {
            return;
        }

        foreach ($value as &$child) {
            self::sortRecursive($child);
        }
        unset($child);

        if (!array_is_list($value)) {
            ksort($value);
        }
    }
}

