<?php

declare(strict_types=1);

namespace App\Domain\Audit;

/**
 * Writes one row to `audit_log` per successful state-changing operation.
 *
 * Two semantics are exposed for the same insert:
 *
 *  - {@see emit()} swallows infra failures and logs them. Use it on
 *    high-volume public paths (`report.received`, `blocklist.requested`)
 *    where a missing audit row is preferable to a user-visible failure
 *    on a request whose primary purpose is not audit.
 *  - {@see emitOrThrow()} propagates infra failures. Use it from inside
 *    `Connection::transactional()` together with the originating
 *    mutation, so a failed audit insert rolls back the state change
 *    that produced it (SEC_REVIEW F4). All admin/auth writes use this
 *    path — without it, an attacker who can intentionally fail the
 *    audit insert mutates state without trace.
 */
interface AuditEmitter
{
    /**
     * Best-effort emit. Catches infra exceptions and logs `audit_emit_failed`.
     *
     * @param array<string, mixed> $payload Free-form event payload, JSON-encoded into details_json.
     *     MUST NOT contain raw secrets (raw tokens, passwords).
     * @param string|null $entityLabel Human-readable identifier (name, slug, IP, CIDR, prefix).
     *     Frozen at write time — later renames don't rewrite history. Pass null only when no
     *     meaningful label exists (system-wide actions like maintenance.purged).
     */
    public function emit(
        string $action,
        ?string $entityType,
        int|string|null $entityId,
        array $payload,
        AuditContext $context,
        ?string $entityLabel = null,
    ): void;

    /**
     * Strict emit. Propagates any infra exception so the caller's enclosing
     * transaction rolls back. Same parameter contract as {@see emit()}.
     *
     * @param array<string, mixed> $payload
     */
    public function emitOrThrow(
        string $action,
        ?string $entityType,
        int|string|null $entityId,
        array $payload,
        AuditContext $context,
        ?string $entityLabel = null,
    ): void;
}
