<?php

namespace App\Support;

use App\Models\RestoreRun;
use Illuminate\Support\Arr;

final class RestoreRunIdempotency
{
    /**
     * @param  array<string, mixed>  $context
     */
    public static function buildKey(int $tenantId, string $operationType, string|int|null $targetId = null, array $context = []): string
    {
        $payload = [
            'managed_environment_id' => $tenantId,
            'operation_type' => trim($operationType),
            'target_id' => $targetId === null ? null : (string) $targetId,
            'context' => self::canonicalize($context),
        ];

        return hash('sha256', json_encode($payload, JSON_THROW_ON_ERROR));
    }

    public static function findActiveRestoreRun(int $tenantId, string $idempotencyKey): ?RestoreRun
    {
        return RestoreRun::query()
            ->where('managed_environment_id', $tenantId)
            ->where('idempotency_key', $idempotencyKey)
            ->whereIn('status', ['queued', 'running'])
            ->latest('id')
            ->first();
    }

    /**
     * Deterministic idempotency key for a live restore execution.
     *
     * @param  array<int>|null  $selectedItemIds
     * @param  array<string, string>  $groupMapping
     */
    public static function restoreExecuteKey(
        int $tenantId,
        int $backupSetId,
        ?array $selectedItemIds,
        array $groupMapping = [],
    ): string {
        $scopeIds = $selectedItemIds;

        if (is_array($scopeIds)) {
            $scopeIds = array_values(array_unique(array_map('intval', $scopeIds)));
            sort($scopeIds);
        }

        return self::buildKey(
            tenantId: $tenantId,
            operationType: 'restore.execute',
            targetId: (string) $backupSetId,
            context: [
                'scope' => $scopeIds,
                'group_mapping' => $groupMapping,
            ],
        );
    }

    /**
     * @param  array<string, mixed>  $value
     * @return array<string, mixed>
     */
    private static function canonicalize(array $value): array
    {
        $value = Arr::map($value, function (mixed $item): mixed {
            if (is_array($item)) {
                /** @var array<string, mixed> $item */
                return static::canonicalize($item);
            }

            return $item;
        });

        ksort($value);

        return $value;
    }
}
