/**
 * Dedup repository: Prisma adapter. The only file in this module that knows
 * about `prisma`. The service depends on `DedupRepository` (interface) so
 * tests can inject an in-memory mock.
 *
 * Atomicity: `INSERT IGNORE` on a PRIMARY KEY column is atomic in MySQL — if
 * a row with the same key exists and is not expired, the second insert is
 * skipped without raising an error. The repository reports back whether a
 * fresh row was actually inserted.
 */

import { prisma } from '@/lib/prisma';

export interface DedupRepository {
    /**
     * Try to claim the key by inserting a fresh entry with expiresAt = now + windowSeconds.
     * Returns true if this caller won the race (key was new or expired and
     * has now been refreshed), false if a non-expired row already exists.
     */
    claim(eventKey: string, windowSeconds: number, now: Date): Promise<boolean>;

    /**
     * Cleanup: drop rows whose expiresAt is in the past. Returns deleted count.
     */
    purgeExpired(now: Date): Promise<number>;
}

export class PrismaDedupRepository implements DedupRepository {
    async claim(eventKey: string, windowSeconds: number, now: Date): Promise<boolean> {
        const expiresAt = new Date(now.getTime() + windowSeconds * 1000);

        // First, opportunistically clear any expired row for this key so the
        // INSERT can succeed when the prior window has passed. Cheap: one
        // primary-key delete that hits zero rows in the happy path.
        await prisma.$executeRawUnsafe(
            `DELETE FROM NotificationDedupe WHERE eventKey = ? AND expiresAt <= ?`,
            eventKey,
            now
        );

        // INSERT IGNORE returns 0 if the row already exists (still live),
        // 1 if the insert succeeded.
        const inserted = await prisma.$executeRawUnsafe(
            `INSERT IGNORE INTO NotificationDedupe (eventKey, expiresAt, createdAt) VALUES (?, ?, ?)`,
            eventKey,
            expiresAt,
            now
        );
        return inserted === 1;
    }

    async purgeExpired(now: Date): Promise<number> {
        const deleted = await prisma.notificationDedupe.deleteMany({
            where: { expiresAt: { lte: now } },
        });
        return deleted.count;
    }
}
