<?php
/**
 * Result value object for {@see GetAuditEventsSummaryUseCase::execute()}.
 *
 * Phase 2 (KI-006 follow-up) immutable VO that wraps the §12.5
 * `GET /affilun/v1/audit/events/summary` response `data` shape
 * documented in `specifications/rest-api-spec.md`. The VO carries:
 *
 *   - `byKind` — a per-`audit_kind` row count map, default-filled
 *     against {@see \Affilun\Infrastructure\Persistence\AuditEventListQuery::VALID_AUDIT_KINDS}
 *     so every known kind has a numeric entry (zero-occurrence
 *     kinds carry `0`). The default-fill is performed by the use
 *     case before constructing the VO so REST consumers can
 *     render a stable histogram without having to know which
 *     kinds the persistence layer happened to omit.
 *
 *   - `recent` — the newest-first projected row slice produced by
 *     {@see \Affilun\Application\Audit\UseCase\ListAuditEventsUseCase::projectRow()}.
 *     Reusing the §12.4 per-row projection keeps the summary
 *     surface consistent with the list surface so the admin /
 *     diagnostics UI can render either with the same row
 *     template.
 *
 *   - `recentLimit` — the limit value the use case actually
 *     applied (after clamping). Echoed back so REST consumers can
 *     render `"showing the most recent N entries"` text without
 *     having to re-derive the bound.
 *
 *   - `total` — the total `affilun_audit_events` row count across
 *     every kind. Computed by the use case as the sum of
 *     `byKind`'s values so the summary card can surface a
 *     headline "N audit events recorded" without an additional
 *     `count()` round-trip.
 *
 * The VO mirrors the
 * {@see \Affilun\Application\Logs\UseCase\GetLogsSummaryResult}
 * shape so the §12.5 audit summary surface reuses the same
 * envelope conventions the §12.3 logs summary surface already
 * established. The REST controller in this slice forwards
 * {@see self::toArray()} verbatim into the §3.1 success
 * envelope's `data` slot.
 *
 * @package Affilun\Application\Audit\UseCase
 */

declare(strict_types=1);

namespace Affilun\Application\Audit\UseCase;

/**
 * Immutable result of {@see GetAuditEventsSummaryUseCase::execute()}.
 *
 * See the file-level docblock for the §12.5 envelope shape and
 * the relationship between the contained `recent` slice and the
 * {@see ListAuditEventsUseCase} per-row projection pipeline.
 */
final class GetAuditEventsSummaryResult {

	/**
	 * Per-`audit_kind` row count map, default-filled against
	 * {@see \Affilun\Infrastructure\Persistence\AuditEventListQuery::VALID_AUDIT_KINDS}.
	 *
	 * Every known kind is present with a numeric value;
	 * zero-occurrence kinds carry `0` rather than being omitted
	 * so REST consumers can render a stable histogram.
	 *
	 * @var array<string, int>
	 */
	private array $byKind;

	/**
	 * Newest-first projected row slice produced by
	 * {@see ListAuditEventsUseCase::projectRow()}.
	 *
	 * Each entry is the §12.4 envelope row carrying
	 * `audit_event_type` / `audit_kind` / `actor` / `target` /
	 * `action` / `result` / `safe_summary` /
	 * `changed_fields_summary` / `request_id` /
	 * `correlation_id` / `job_id` / `created_at` slots.
	 *
	 * @var list<array<string, mixed>>
	 */
	private array $recent;

	/**
	 * Effective recent-limit value the use case applied after
	 * clamping, echoed back so REST consumers can render
	 * `"showing the most recent N entries"` text without
	 * re-deriving the bound.
	 */
	private int $recentLimit;

	/**
	 * Total `affilun_audit_events` row count across every kind.
	 *
	 * Computed by the use case as the sum of `$byKind`'s values
	 * so the summary card can surface a headline
	 * `"N audit events recorded"` without an additional
	 * {@see \Affilun\Infrastructure\Persistence\AuditEventRepository::count()}
	 * round-trip.
	 */
	private int $total;

	/**
	 * Construct the result with explicit values for every axis.
	 *
	 * @param array<string, int>         $byKind      Per-kind row count map.
	 * @param list<array<string, mixed>> $recent      Projected row slice.
	 * @param int                        $recentLimit Effective recent-limit.
	 * @param int                        $total       Total row count across every kind.
	 */
	public function __construct(
		array $byKind,
		array $recent,
		int $recentLimit,
		int $total
	) {
		$this->byKind      = $byKind;
		$this->recent      = array_values( $recent );
		$this->recentLimit = $recentLimit;
		$this->total       = $total;
	}

	/**
	 * Per-`audit_kind` row count map, default-filled against
	 * {@see \Affilun\Infrastructure\Persistence\AuditEventListQuery::VALID_AUDIT_KINDS}.
	 *
	 * @return array<string, int>
	 */
	public function byKind(): array {
		return $this->byKind;
	}

	/**
	 * Newest-first projected row slice.
	 *
	 * @return list<array<string, mixed>>
	 */
	public function recent(): array {
		return $this->recent;
	}

	/**
	 * Effective recent-limit value applied after clamping.
	 */
	public function recentLimit(): int {
		return $this->recentLimit;
	}

	/**
	 * Total `affilun_audit_events` row count across every kind.
	 */
	public function total(): int {
		return $this->total;
	}

	/**
	 * Render the §12.5 success envelope `data` shape:
	 *
	 *   `array(
	 *       'by_kind'      => array<string, int>,
	 *       'recent'       => list<array<string, mixed>>,
	 *       'recent_limit' => int,
	 *       'total'        => int,
	 *   )`
	 *
	 * @return array{
	 *     by_kind: array<string, int>,
	 *     recent: list<array<string, mixed>>,
	 *     recent_limit: int,
	 *     total: int,
	 * }
	 */
	public function toArray(): array {
		return array(
			'by_kind'      => $this->byKind,
			'recent'       => $this->recent,
			'recent_limit' => $this->recentLimit,
			'total'        => $this->total,
		);
	}
}
