/**
 * HMAC-signed OAuth interstitial 2FA verification cookie — unit tests.
 *
 * Replaces the in-memory verification state from the initial PR #137
 * with a fully stateless, distributed-safe design. Each request brings
 * its own signed proof; no shared Map.
 *
 * Properties tested:
 *   - happy path: build → verify with matching userId + fresh TTL → true.
 *   - cross-user: signed for userId A → verify with userId B → false.
 *   - expired: TTL elapsed → false.
 *   - tampered signature: any change to the sig portion → false.
 *   - tampered payload: any change to the payload portion → false (sig
 *     no longer matches).
 *   - malformed values (missing dot, empty, garbage) → false.
 */
process.env.NEXTAUTH_SECRET = process.env.NEXTAUTH_SECRET || 'this-is-a-long-enough-secret-for-tests';

jest.mock('@/lib/env', () => ({
    env: {
        NEXTAUTH_SECRET: 'this-is-a-long-enough-secret-for-tests',
    },
}));

import crypto from 'crypto';
import {
    buildOauth2faVerifiedCookie,
    verifyOauth2faVerifiedCookie,
    OAUTH_2FA_COOKIE_NAME,
} from '../oauth-2fa-cookie';

describe('OAuth 2FA verification cookie', () => {
    it('cookie name uses __Host- prefix (forces path=/, secure, no domain)', () => {
        expect(OAUTH_2FA_COOKIE_NAME.startsWith('__Host-')).toBe(true);
    });

    it('build returns HttpOnly + Secure + SameSite=Strict + path=/', () => {
        const { options } = buildOauth2faVerifiedCookie(42);
        expect(options.httpOnly).toBe(true);
        expect(options.secure).toBe(true);
        expect(options.sameSite).toBe('strict');
        expect(options.path).toBe('/');
        expect(options.maxAge).toBeGreaterThan(0);
    });

    it('happy path: sign for userId 42 → verify with 42 → true', () => {
        const { value } = buildOauth2faVerifiedCookie(42);
        expect(verifyOauth2faVerifiedCookie(value, 42)).toBe(true);
    });

    it('cross-user mismatch: sign for 42 → verify with 99 → false', () => {
        const { value } = buildOauth2faVerifiedCookie(42);
        expect(verifyOauth2faVerifiedCookie(value, 99)).toBe(false);
    });

    it('expired: TTL elapsed → false', () => {
        const { value } = buildOauth2faVerifiedCookie(42, 1); // 1ms TTL
        // Use a `now` argument far in the future to make the test deterministic.
        const future = Date.now() + 10_000;
        expect(verifyOauth2faVerifiedCookie(value, 42, future)).toBe(false);
    });

    it('tampered signature: flip last char of sig → false', () => {
        const { value } = buildOauth2faVerifiedCookie(42);
        // Replace the last character (in the signature segment).
        const bad = value.slice(0, -1) + (value.endsWith('a') ? 'b' : 'a');
        expect(verifyOauth2faVerifiedCookie(bad, 42)).toBe(false);
    });

    it('tampered payload: change a byte in the payload → false', () => {
        const { value } = buildOauth2faVerifiedCookie(42);
        const dot = value.lastIndexOf('.');
        const payload = value.slice(0, dot);
        const sig = value.slice(dot + 1);
        const bad = (payload[0] === 'a' ? 'b' : 'a') + payload.slice(1) + '.' + sig;
        expect(verifyOauth2faVerifiedCookie(bad, 42)).toBe(false);
    });

    it('missing dot → false', () => {
        expect(verifyOauth2faVerifiedCookie('nodothere', 42)).toBe(false);
    });

    it('empty / undefined → false', () => {
        expect(verifyOauth2faVerifiedCookie('', 42)).toBe(false);
        expect(verifyOauth2faVerifiedCookie(undefined, 42)).toBe(false);
    });

    it('payload with wrong userId type → false', () => {
        // Build by hand: payload claims userId is a string.
        const secret = 'this-is-a-long-enough-secret-for-tests';
        const payload = Buffer.from(
            JSON.stringify({ userId: 'forty-two', expiresAt: Date.now() + 60_000 }),
            'utf8',
        ).toString('base64url');
        const sig = crypto.createHmac('sha256', secret).update(payload).digest('base64url');
        expect(verifyOauth2faVerifiedCookie(`${payload}.${sig}`, 42)).toBe(false);
    });
});
