<?php

namespace Modules\Payment\Support;

/**
 * KBZ's signing scheme, ported verbatim from bnf_event's `KBZPay::joinKeyVal`/
 * `signature` (domain.md §6): flatten the request array (excluding `sign`/
 * `sign_type`, at any nesting level — `biz_content` included) into sorted
 * `key=val` pairs joined by `&`, append `&key={merchant_key}`, SHA-256 hash,
 * uppercase. Shared by initiate()/verify()/refund() on every gateway.
 */
class KbzSignature
{
    /**
     * @param  array<string, mixed>  $data
     * @param  list<string>  $skips  Additional top-level/nested keys to exclude beyond sign/sign_type.
     */
    public static function joinKeyVal(array $data, array $skips = []): string
    {
        $skips = [...$skips, 'sign', 'sign_type'];

        $fields = [];
        self::collect($data, $skips, $fields);

        usort($fields, fn (array $a, array $b): int => strcmp($a['key'], $b['key']));

        $pairs = [];
        foreach ($fields as $field) {
            if ($field['val'] !== null && trim((string) $field['val']) !== '') {
                $pairs[] = $field['key'].'='.$field['val'];
            }
        }

        return implode('&', $pairs);
    }

    /**
     * @param  array<string, mixed>  $data
     * @param  list<string>  $skips
     */
    public static function sign(array $data, string $merchantKey, array $skips = []): string
    {
        $joined = self::joinKeyVal($data, $skips);

        return strtoupper(hash('sha256', $joined.'&key='.$merchantKey));
    }

    /**
     * @param  list<string>  $skips
     * @param  list<array{key: string, val: mixed}>  $fields
     */
    private static function collect(mixed $value, array $skips, array &$fields, string $key = ''): void
    {
        if (in_array($key, $skips, true)) {
            return;
        }

        if (is_array($value)) {
            foreach ($value as $subKey => $subVal) {
                self::collect($subVal, $skips, $fields, (string) $subKey);
            }

            return;
        }

        if ($key === '') {
            return;
        }

        $fields[] = ['key' => $key, 'val' => $value];
    }
}
