<?php

declare(strict_types=1);

namespace App\Shared\Infrastructure\Audit;

use DateTimeImmutable;

use const JSON_THROW_ON_ERROR;

use Ramsey\Uuid\Uuid;
use Ramsey\Uuid\UuidInterface;

/**
 * Read model for audit log entries.
 *
 * Immutable DTO representing a single audit log entry.
 */
final readonly class AuditLogEntry
{
    /**
     * @param array<string, mixed> $payload
     * @param array<string, mixed> $metadata
     */
    public function __construct(
        public UuidInterface $id,
        public string $aggregateType,
        public ?UuidInterface $aggregateId,
        public string $eventType,
        public array $payload,
        public array $metadata,
        public DateTimeImmutable $occurredAt,
        public int $sequenceNumber,
    ) {
    }

    /**
     * @param array<string, mixed> $row
     */
    public static function fromDatabaseRow(array $row): self
    {
        /** @var string $id */
        $id = $row['id'];
        /** @var string $aggregateType */
        $aggregateType = $row['aggregate_type'];
        /** @var string|null $aggregateId */
        $aggregateId = $row['aggregate_id'];
        /** @var string $eventType */
        $eventType = $row['event_type'];
        /** @var string $payloadJson */
        $payloadJson = $row['payload'];
        /** @var string $metadataJson */
        $metadataJson = $row['metadata'];
        /** @var string $occurredAt */
        $occurredAt = $row['occurred_at'];
        /** @var string|int $sequenceNumber */
        $sequenceNumber = $row['sequence_number'];

        /** @var array<string, mixed> $payload */
        $payload = json_decode($payloadJson, true, 512, JSON_THROW_ON_ERROR);
        /** @var array<string, mixed> $metadata */
        $metadata = json_decode($metadataJson, true, 512, JSON_THROW_ON_ERROR);

        return new self(
            id: Uuid::fromString($id),
            aggregateType: $aggregateType,
            aggregateId: $aggregateId !== null
                ? Uuid::fromString($aggregateId)
                : null,
            eventType: $eventType,
            payload: $payload,
            metadata: $metadata,
            occurredAt: new DateTimeImmutable($occurredAt),
            sequenceNumber: (int) $sequenceNumber,
        );
    }

    public function correlationId(): ?string
    {
        /** @var string|null $correlationId */
        $correlationId = $this->metadata['correlation_id'] ?? null;

        return $correlationId;
    }

    public function tenantId(): ?string
    {
        /** @var string|null $tenantId */
        $tenantId = $this->metadata['tenant_id'] ?? null;

        return $tenantId;
    }

    public function userId(): ?string
    {
        /** @var string|null $userId */
        $userId = $this->metadata['user_id'] ?? null;

        return $userId;
    }
}
