/**
 * In-memory stub of the prisma.bruteForceCounter delegate, used by the
 * login-attempts + twofa-attempts unit tests. Mirrors only the methods
 * brute-force-counter.ts actually calls, with the upsert / findUnique
 * / update / delete / deleteMany semantics MySQL would give us.
 *
 * Kept inside the auth tests dir so it doesn't leak into prod imports.
 */
interface Row {
    id: number;
    bucket: string;
    bucketKey: string;
    failures: number[];
    lockedUntil: Date | null;
    windowMs: number;
}

interface Compound {
    bucket: string;
    bucketKey: string;
}

const store = new Map<string, Row>();
let nextId = 1;

function compoundKey(c: Compound): string {
    return `${c.bucket}:${c.bucketKey}`;
}

export const bruteForceCounterMock = {
    findUnique({ where }: { where: { bucket_bucketKey: Compound } }) {
        return Promise.resolve(store.get(compoundKey(where.bucket_bucketKey)) ?? null);
    },

    update({ where, data }: {
        where: { bucket_bucketKey: Compound };
        data: { failures?: number[]; lockedUntil?: Date | null; windowMs?: number };
    }) {
        const key = compoundKey(where.bucket_bucketKey);
        const existing = store.get(key);
        if (!existing) throw new Error(`update: row not found ${key}`);
        const next: Row = {
            ...existing,
            failures: data.failures ?? existing.failures,
            lockedUntil: data.lockedUntil === undefined ? existing.lockedUntil : data.lockedUntil,
            windowMs: data.windowMs ?? existing.windowMs,
        };
        store.set(key, next);
        return Promise.resolve(next);
    },

    upsert({ where, create, update }: {
        where: { bucket_bucketKey: Compound };
        create: Omit<Row, 'id'>;
        update: { failures: number[]; lockedUntil: Date | null; windowMs: number };
    }) {
        const key = compoundKey(where.bucket_bucketKey);
        const existing = store.get(key);
        if (existing) {
            const next: Row = { ...existing, ...update };
            store.set(key, next);
            return Promise.resolve(next);
        }
        const created: Row = { id: nextId++, ...create };
        store.set(key, created);
        return Promise.resolve(created);
    },

    delete({ where }: { where: { bucket_bucketKey: Compound } }) {
        const key = compoundKey(where.bucket_bucketKey);
        const existing = store.get(key);
        store.delete(key);
        return Promise.resolve(existing ?? null);
    },

    deleteMany({ where }: {
        where: { bucket?: string | { in: string[] }; bucketKey?: string | { startsWith: string } };
    }) {
        let deleted = 0;
        for (const [key, row] of store.entries()) {
            const bucketMatch = !where.bucket
                || (typeof where.bucket === 'string' && row.bucket === where.bucket)
                || (typeof where.bucket === 'object' && where.bucket.in.includes(row.bucket));
            const keyMatch = !where.bucketKey
                || (typeof where.bucketKey === 'string' && row.bucketKey === where.bucketKey)
                || (typeof where.bucketKey === 'object' && row.bucketKey.startsWith(where.bucketKey.startsWith));
            if (bucketMatch && keyMatch) {
                store.delete(key);
                deleted++;
            }
        }
        return Promise.resolve({ count: deleted });
    },
};

export function _resetMockStore(): void {
    store.clear();
    nextId = 1;
}
