*/ public function findPriorSubmissions(FormSubmission $current): Collection { try { $email = $this->normaliseEmail($current->public_submitter_email); if ($email === null) { return new Collection; } if ($current->form_schema_id === null || $current->id === null) { return new Collection; } return FormSubmission::query() ->where('form_schema_id', $current->form_schema_id) ->where('status', FormSubmissionStatus::SUBMITTED->value) ->whereRaw('LOWER(TRIM(public_submitter_email)) = ?', [$email]) ->where('id', '!=', $current->id) ->orderBy('submitted_at') ->get(); } catch (Throwable $e) { Log::error('form-builder.duplicate-detector.failed', [ 'submission_id' => $current->id ?? null, 'message' => $e->getMessage(), ]); return new Collection; } } /** * Shape the detector output for the public submission response. * Returns null when no priors exist (the common case) so the * resource can render a nullable block. * * @return array{count: int, first_submitted_at: string}|null */ public function formatForResponse(FormSubmission $current): ?array { $priors = $this->findPriorSubmissions($current); if ($priors->isEmpty()) { return null; } $first = $priors->first(); return [ 'count' => $priors->count(), 'first_submitted_at' => optional($first->submitted_at)->toIso8601String() ?? '', ]; } private function normaliseEmail(?string $raw): ?string { if ($raw === null) { return null; } $normalised = strtolower(trim($raw)); return $normalised === '' ? null : $normalised; } }