<?php

namespace GriffinForms\Includes\Helpers;

final class Compliance
{
    /**
     * Canonicalize an array for stable hashing.
     *
     * - Assoc arrays are sorted by key.
     * - Indexed arrays keep order but normalize values.
     */
    public static function canonicalize($value)
    {
        if (is_array($value)) {
            if (self::isAssoc($value)) {
                ksort($value);
            }

            $normalized = [];
            foreach ($value as $key => $val) {
                $normalized[$key] = self::canonicalize($val);
            }

            return $normalized;
        }

        return $value;
    }

    /**
     * Remove volatile keys from a nested array before hashing.
     */
    public static function stripKeysRecursive(array $data, array $keys): array
    {
        $keys = array_map('strval', $keys);

        foreach ($data as $key => $value) {
            if (in_array((string) $key, $keys, true)) {
                unset($data[$key]);
                continue;
            }

            if (is_array($value)) {
                $data[$key] = self::stripKeysRecursive($value, $keys);
            }
        }

        return $data;
    }

    /**
     * Canonical JSON encoding for stable hashing.
     */
    public static function canonicalJson($value): string
    {
        $canonical = self::canonicalize($value);
        return (string) wp_json_encode($canonical, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
    }

    public static function hashLayout($layout): string
    {
        return hash('sha256', self::canonicalJson($layout));
    }

    public static function hashSubmission($submission): string
    {
        return hash('sha256', self::canonicalJson($submission));
    }

    /**
     * Resolve effective compliance flags based on mode + global defaults.
     *
     * @param array $flags
     * @return array{mode:string,store_ip:int,store_user_agent:int,retention_days:int}
     */
    public static function resolveFlags(array $flags): array
    {
        $mode = isset($flags['mode']) ? (string) $flags['mode'] : '';
        if (!in_array($mode, array('inherit', 'custom'), true)) {
            $mode = empty($flags) ? 'inherit' : 'custom';
        }

        $settings = \GriffinForms\Settings::getInstance();
        $defaults_enabled = (int) $settings->getOption('compliance_defaults_enabled', 0);
        $defaults = array(
            'store_ip' => $defaults_enabled ? (int) $settings->getOption('compliance_default_store_ip', 0) : 0,
            'store_user_agent' => $defaults_enabled ? (int) $settings->getOption('compliance_default_store_user_agent', 0) : 0,
            'retention_days' => $defaults_enabled ? (int) $settings->getOption('compliance_default_retention_days', 0) : 0
        );

        if ($mode === 'inherit') {
            return array_merge(array('mode' => 'inherit'), $defaults);
        }

        return array(
            'mode' => 'custom',
            'store_ip' => !empty($flags['store_ip']) ? 1 : 0,
            'store_user_agent' => !empty($flags['store_user_agent']) ? 1 : 0,
            'retention_days' => isset($flags['retention_days']) ? max(0, absint($flags['retention_days'])) : 0
        );
    }

    private static function isAssoc(array $array): bool
    {
        $keys = array_keys($array);
        return $keys !== array_keys($keys);
    }
}
