import { beforeEach, describe, expect, it } from 'vitest';
import { handleActuateAPI } from '../../api/index.js';
import { initDB } from '../../db.js';
import { generateApiKey } from '../../security/api-key-enhanced.js';
/**
 * Contract tests for the Security Center endpoints:
 *   - GET  /security                     → authed (admin), computed posture.
 *   - PUT  /security                     → persists enforced policy, lockout-safe.
 *   - POST /security/sessions/revoke-all → revokes the caller's other sessions.
 *
 * Proves the response never leaks secret values, that lockout-safe MFA enabling
 * is enforced server-side, and that policy round-trips through the settings global.
 */
const VALID_SECRET = 'a'.repeat(48);
function createMockDB(state, apiKey) {
    const tx = {
        document: {
            update: async ({ data }) => {
                state.settings = data.data;
                return { id: 'settings-doc', data: state.settings };
            },
            create: async ({ data }) => {
                state.settings = data.data;
                return { id: 'settings-doc', data: state.settings };
            },
        },
        version: { create: async () => ({ id: 'v1' }) },
    };
    return {
        apiKey: {
            findMany: async () => (apiKey ? [apiKey] : []),
            findUnique: async ({ where }) => apiKey && apiKey.keyHash === where.keyHash ? apiKey : null,
            update: async () => apiKey,
        },
        document: {
            findFirst: async () => ({ id: 'settings-doc', data: state.settings }),
            findMany: async () => [],
            count: async () => 0,
        },
        version: { create: async () => ({ id: 'v1' }) },
        user: {
            findMany: async () => [
                { role: 'ADMIN', isActive: true, totpEnabled: state.viewerHasMfa },
                { role: 'EDITOR', isActive: true, totpEnabled: false },
            ],
            findUnique: async () => ({ totpEnabled: state.viewerHasMfa }),
            count: async () => 1,
        },
        session: {
            findMany: async () => [],
            count: async () => 2,
            updateMany: async (args) => {
                state.revokeAllArgs = args;
                return { count: state.revokeAllResult };
            },
        },
        auditLog: {
            count: async () => 3,
            create: async () => ({ id: 'log1' }),
        },
        $transaction: async (fn) => fn(tx),
    };
}
async function makeKey(scopes) {
    const { key, keyHash, keyPrefix } = await generateApiKey({ prefix: 'act_sk', scopes });
    return {
        key,
        record: {
            id: 'apikey-1',
            keyHash,
            keyPrefix,
            userId: 'user-1',
            scopes,
            ipRestrictions: null,
            expiresAt: null,
            lastUsedAt: null,
            revokedAt: null,
        },
    };
}
function freshState() {
    return {
        settings: { siteTitle: 'Actuate Media' },
        viewerHasMfa: false,
        revokeAllResult: 2,
    };
}
beforeEach(() => {
    delete globalThis.__actuateConfig;
    process.env.CMS_SECRET = VALID_SECRET;
});
describe('GET /security', () => {
    it('requires authentication', async () => {
        const db = createMockDB(freshState());
        initDB(db);
        const handler = handleActuateAPI({ prismaClient: db, config: { collections: {} } });
        const res = await handler(new Request('https://example.com/api/cms/security'));
        expect(res.status).toBe(401);
    });
    it('rejects non-admin API keys', async () => {
        const { key, record } = await makeKey({ media: true });
        const db = createMockDB(freshState(), record);
        initDB(db);
        const handler = handleActuateAPI({ prismaClient: db, config: { collections: {} } });
        const res = await handler(new Request('https://example.com/api/cms/security', {
            headers: { Authorization: `Bearer ${key}` },
        }));
        expect(res.status).toBe(403);
    });
    it('returns computed posture + summaries and never leaks secret values', async () => {
        const { key, record } = await makeKey({ admin: true });
        const db = createMockDB(freshState(), record);
        initDB(db);
        const handler = handleActuateAPI({ prismaClient: db, config: { collections: {} } });
        const res = await handler(new Request('https://example.com/api/cms/security', {
            headers: { Authorization: `Bearer ${key}` },
        }));
        expect(res.status).toBe(200);
        const body = (await res.json());
        expect(body.data.status.status).toBeDefined();
        expect(Array.isArray(body.data.status.checks)).toBe(true);
        expect(body.data.users.totalAdmins).toBe(1);
        expect(body.data.secrets.cmsSecret).toBe('pass');
        expect(body.data.activeSessions).toBe(2);
        expect(body.data.failedLogins24h).toBe(3);
        // The raw secret value must never appear anywhere in the payload.
        expect(JSON.stringify(body)).not.toContain(VALID_SECRET);
    });
});
describe('PUT /security', () => {
    it('persists the enforced policy and reflects it on read-back', async () => {
        const { key, record } = await makeKey({ admin: true });
        const state = freshState();
        const db = createMockDB(state, record);
        initDB(db);
        const handler = handleActuateAPI({ prismaClient: db, config: { collections: {} } });
        const res = await handler(new Request('https://example.com/api/cms/security', {
            method: 'PUT',
            headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
            body: JSON.stringify({
                mfa: { required: false, gracePeriodDays: 14 },
                session: { maxConcurrentSessions: 3 },
            }),
        }));
        expect(res.status).toBe(200);
        const body = (await res.json());
        expect(body.data.settings.session.maxConcurrentSessions).toBe(3);
        expect(body.data.settings.mfa.gracePeriodDays).toBe(14);
        // Persisted into the settings global without clobbering siblings.
        expect(state.settings.security.session.maxConcurrentSessions).toBe(3);
        expect(state.settings.siteTitle).toBe('Actuate Media');
    });
    it('blocks enabling MFA when the acting admin has no MFA (lockout-safe)', async () => {
        const { key, record } = await makeKey({ admin: true });
        const state = freshState();
        state.viewerHasMfa = false;
        const db = createMockDB(state, record);
        initDB(db);
        const handler = handleActuateAPI({ prismaClient: db, config: { collections: {} } });
        const res = await handler(new Request('https://example.com/api/cms/security', {
            method: 'PUT',
            headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
            body: JSON.stringify({
                mfa: { required: true, gracePeriodDays: 7 },
                session: { maxConcurrentSessions: 5 },
            }),
        }));
        expect(res.status).toBe(409);
        const body = (await res.json());
        expect(body.code).toBe('MFA_LOCKOUT_RISK');
        // Nothing should have been persisted.
        expect(state.settings.security).toBeUndefined();
    });
    it('allows enabling MFA when the acting admin has MFA', async () => {
        const { key, record } = await makeKey({ admin: true });
        const state = freshState();
        state.viewerHasMfa = true;
        const db = createMockDB(state, record);
        initDB(db);
        const handler = handleActuateAPI({ prismaClient: db, config: { collections: {} } });
        const res = await handler(new Request('https://example.com/api/cms/security', {
            method: 'PUT',
            headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
            body: JSON.stringify({
                mfa: { required: true, gracePeriodDays: 7 },
                session: { maxConcurrentSessions: 5 },
            }),
        }));
        expect(res.status).toBe(200);
        expect(state.settings.security.mfa.required).toBe(true);
        expect(typeof state.settings.security.mfa.requiredAt).toBe('string');
    });
});
describe('POST /security/sessions/revoke-all', () => {
    it("revokes the caller's other sessions and returns the count", async () => {
        const { key, record } = await makeKey({ admin: true });
        const state = freshState();
        const db = createMockDB(state, record);
        initDB(db);
        const handler = handleActuateAPI({ prismaClient: db, config: { collections: {} } });
        const res = await handler(new Request('https://example.com/api/cms/security/sessions/revoke-all', {
            method: 'POST',
            headers: { Authorization: `Bearer ${key}` },
        }));
        expect(res.status).toBe(200);
        const body = (await res.json());
        expect(body.data.revoked).toBe(2);
        // Excludes the current session id.
        expect(state.revokeAllArgs?.where?.id?.not).toBe('apikey-1');
    });
});
//# sourceMappingURL=security-routes.test.js.map