<?php

declare(strict_types=1);

namespace App\Services\Audit;

use App\Models\AuditLog;
use App\Models\ManagedEnvironment;
use App\Models\Workspace;
use App\Support\Audit\AuditActionId;
use App\Support\Audit\AuditActorSnapshot;
use App\Support\Audit\AuditOutcome;
use App\Support\Audit\AuditTargetSnapshot;
use Carbon\CarbonInterface;

final class AuditRecorder
{
    public function __construct(
        private readonly AuditEventBuilder $builder,
    ) {}

    /**
     * @param  array<string, mixed>  $context
     */
    public function record(
        string|AuditActionId $action,
        array $context = [],
        ?Workspace $workspace = null,
        ?ManagedEnvironment $tenant = null,
        ?AuditActorSnapshot $actor = null,
        ?AuditTargetSnapshot $target = null,
        string|AuditOutcome|null $outcome = null,
        ?CarbonInterface $recordedAt = null,
        ?string $summary = null,
        ?int $operationRunId = null,
    ): AuditLog {
        $actionValue = $action instanceof AuditActionId ? $action->value : trim($action);

        $metadata = is_array($context['metadata'] ?? null) ? $context['metadata'] : [];
        $dedupeKey = is_string($metadata['_dedupe_key'] ?? null) ? trim((string) $metadata['_dedupe_key']) : null;

        if ($dedupeKey !== '') {
            $metadata['_dedupe_key'] = $dedupeKey;
            $context['metadata'] = $metadata;
        }

        $attributes = $this->builder->buildRecordAttributes(
            action: $actionValue,
            context: $context,
            workspace: $workspace,
            tenant: $tenant,
            actor: $actor,
            target: $target,
            outcome: $outcome,
            recordedAt: $recordedAt,
            summary: $summary,
            operationRunId: $operationRunId,
        );

        if ($dedupeKey !== null && $dedupeKey !== '') {
            $existing = AuditLog::query()
                ->where('managed_environment_id', $attributes['managed_environment_id'])
                ->where('action', $attributes['action'])
                ->where('resource_type', $attributes['resource_type'])
                ->where('resource_id', $attributes['resource_id'])
                ->whereRaw("metadata ->> '_dedupe_key' = ?", [$dedupeKey])
                ->latest('id')
                ->first();

            if ($existing instanceof AuditLog) {
                return $existing;
            }
        }

        return AuditLog::query()->create($attributes);
    }
}
