<?php

declare(strict_types=1);

namespace App\Services;

use App\Models\Deal;
use Illuminate\Support\Carbon;

/**
 * Антифрод-дедуп лидов по `(tenant_id, phone)` в окне 24 ч (Биз-19, §10.8.1).
 *
 * Цель: в pay-per-lead-сегменте поставщик может прислать одно физлицо дважды
 * (двойной submit формы / повторный звонок) — без защиты клиент платит за оба.
 *
 * Стратегия: ищем master-сделку (запись без `duplicate_of_id`) с тем же
 * `(tenant_id, phone)` и `received_at >= NOW() - INTERVAL '24 hours'`.
 * Если найдена — новая сделка получает `duplicate_of_id = master.id` и
 * НЕ списывает с баланса.
 *
 * Окно фиксированное 24 ч (не настраивается на MVP) — компромисс между
 * антифродом и легитимными повторными интересами.
 *
 * Цепочки не строятся: дубль ссылается ТОЛЬКО на master (запись без
 * `duplicate_of_id`), не на другой дубль. Если master найден среди дублей —
 * берётся его собственный `duplicate_of_id` (root master).
 *
 * Performance: существующий индекс `(tenant_id, phone)` достаточен, см. §10.8.1.
 */
class DuplicateDetector
{
    public const WINDOW_HOURS = 24;

    /**
     * Поиск master-сделки для (tenantId, phone) в окне 24 ч.
     *
     * Возвращает Deal-объект master'а либо null если master не найден.
     * Текущий момент `now` параметризуется для тестируемости — в production
     * по умолчанию `Carbon::now()`.
     */
    public function findMaster(int $tenantId, string $phone, ?Carbon $now = null): ?Deal
    {
        $now ??= Carbon::now();
        $windowStart = $now->copy()->subHours(self::WINDOW_HOURS);

        return Deal::query()
            ->where('tenant_id', $tenantId)
            ->where('phone', $phone)
            ->where('received_at', '>=', $windowStart)
            ->whereNull('duplicate_of_id')
            ->orderBy('received_at')
            ->first();
    }
}
