<?php

declare(strict_types=1);

namespace Plugin\tnt_mcp\src\Mcp\Support;

/**
 * Masks credential-shaped argument values before they are persisted.
 *
 * Every store that keeps a copy of a tool call's arguments — the audit table, the change journal —
 * has to run its input through here first. The audit table is the reason this became shared: it kept
 * `json_encode($args)` verbatim, so one connector's `set_setting({value: "<smtp password>"})` was
 * readable by any other token that could reach the table. (The table is denied to `run_sql_read`
 * outright now as well; that is a second lock on the same door, not a replacement for this one.)
 *
 * Keyed on the argument *name*, like {@see \Plugin\tnt_mcp\src\Mcp\Db\ResultShaper} is on the column
 * name, and recursive so a secret nested in a map or list is caught too.
 */
final class ArgRedactor
{
    public const SECRET_KEY_PATTERN = '/(pass|token|secret|hash|apikey|key)/i';

    public const MASK = '***';

    /**
     * @param array<array-key,mixed> $args
     * @return array<array-key,mixed>
     */
    public static function redact(array $args): array
    {
        $out = [];
        foreach ($args as $key => $value) {
            if (\is_string($key) && \preg_match(self::SECRET_KEY_PATTERN, $key) === 1) {
                $out[$key] = self::MASK;
                continue;
            }
            $out[$key] = \is_array($value) ? self::redact($value) : $value;
        }

        return $out;
    }
}
