/**
 * Per-(userId, IP) 2FA verification attempt tracker.
 *
 * audit3-followup (2026-05-29): backed by MySQL via BruteForceCounter so
 * state survives restarts. See login-attempts.ts for the rationale.
 *
 * Two buckets used:
 *   - 'twofa-ip'   keyed by `${userId}|${ip}` — defeats single-IP brute force
 *   - 'twofa-user' keyed by `${userId}`       — defeats distributed brute force
 *                                               (one IP per request)
 *
 * Public API preserved (sync → async); the only callsite change is the
 * addition of `await`.
 */
import {
    isLocked as bfcIsLocked,
    recordFailure as bfcRecordFailure,
    recordSuccess as bfcRecordSuccess,
    recordSuccessByPrefix as bfcRecordSuccessByPrefix,
    type LockStatus,
    type CounterConfig,
} from './brute-force-counter';
import { prisma } from '@/lib/prisma';

export type { LockStatus } from './brute-force-counter';

const IP_BUCKET = 'twofa-ip';
const USER_BUCKET = 'twofa-user';

const DEFAULTS: CounterConfig = {
    maxFailures: 5,
    windowMs: 15 * 60 * 1000,
    lockoutMs: 15 * 60 * 1000,
};

export interface Config {
    maxFailures?: number;
    windowMs?: number;
    lockoutMs?: number;
}

function resolveConfig(cfg: Config): CounterConfig {
    return {
        maxFailures: cfg.maxFailures ?? DEFAULTS.maxFailures,
        windowMs: cfg.windowMs ?? DEFAULTS.windowMs,
        lockoutMs: cfg.lockoutMs ?? DEFAULTS.lockoutMs,
    };
}

function ipKey(userId: number, ip: string): string {
    return `${userId}|${ip}`;
}

function userKey(userId: number): string {
    return `${userId}`;
}

/** Inspect lock state for this (userId, ip) pair AND for the userId on its own. */
export async function isLocked(userId: number, ip: string, cfg: Config = {}): Promise<LockStatus> {
    const resolved = resolveConfig(cfg);
    const ipStatus = await bfcIsLocked(IP_BUCKET, ipKey(userId, ip), resolved);
    if (ipStatus.locked) return ipStatus;
    return bfcIsLocked(USER_BUCKET, userKey(userId), resolved);
}

/** Record one failed 2FA attempt. */
export async function recordFailure(userId: number, ip: string, cfg: Config = {}): Promise<void> {
    const resolved = resolveConfig(cfg);
    await bfcRecordFailure(IP_BUCKET, ipKey(userId, ip), resolved);
    await bfcRecordFailure(USER_BUCKET, userKey(userId), resolved);
}

/** Clear all state for this userId — call after a successful verification. */
export async function recordSuccess(userId: number, ip?: string): Promise<void> {
    await bfcRecordSuccess(USER_BUCKET, userKey(userId));
    if (ip) {
        await bfcRecordSuccess(IP_BUCKET, ipKey(userId, ip));
    }
    // Drop any other IP-keyed rows for this userId — a real login
    // succeeded, so other ongoing brute-force attempts no longer need
    // their counters preserved.
    await bfcRecordSuccessByPrefix(IP_BUCKET, `${userId}|`);
}

/** Test-only: drop all state in both buckets. */
export async function _resetForTests(): Promise<void> {
    await prisma.bruteForceCounter.deleteMany({
        where: { bucket: { in: [IP_BUCKET, USER_BUCKET] } },
    });
}
