import { afterEach, beforeEach, describe, expect, it } from 'vitest';
import { handleActuateAPI } from '../../api/index.js';
import { createSession } from '../../auth/session.js';
import { initDB } from '../../db.js';
/**
 * Integration tests for `GET /api/cms/users/search`.
 *
 * The endpoint exists for the comment mention picker (Phase 7
 * carry-over) — every authenticated user can query it, but the result
 * set is capped at 10 rows and trimmed to `{ id, name, email }` so
 * the picker can never become a directory-enumeration tool or leak
 * privileged fields (role, hashes, audit metadata).
 */
const SECRET = 'test-secret-for-users-search-routes-1234567890abc';
function createMockDB(rows) {
    return {
        session: { findUnique: async () => ({ revokedAt: null }) },
        user: {
            async findMany({ where, select, orderBy, take, }) {
                let r = rows;
                if (where?.isActive !== undefined) {
                    r = r.filter((u) => u.isActive === where.isActive);
                }
                if (where?.isApproved !== undefined) {
                    // Default test rows to approved so existing assertions
                    // keep working; explicit `isApproved: false` lets a new
                    // test exercise the filter.
                    r = r.filter((u) => (u.isApproved ?? true) === where.isApproved);
                }
                if (where?.OR && where.OR.length > 0) {
                    // Match the production semantics: case-insensitive
                    // `contains` against name OR email.
                    r = r.filter((u) => {
                        for (const clause of where.OR) {
                            const nameQ = clause.name?.contains?.toLowerCase();
                            const emailQ = clause.email?.contains?.toLowerCase();
                            if (nameQ && u.name.toLowerCase().includes(nameQ))
                                return true;
                            if (emailQ && u.email.toLowerCase().includes(emailQ))
                                return true;
                        }
                        return false;
                    });
                }
                if (orderBy?.name === 'asc')
                    r = [...r].sort((a, b) => a.name.localeCompare(b.name));
                if (typeof take === 'number')
                    r = r.slice(0, take);
                if (select) {
                    return r.map((u) => {
                        const out = {};
                        for (const k of Object.keys(select)) {
                            out[k] = u[k];
                        }
                        return out;
                    });
                }
                return r;
            },
        },
    };
}
async function authHeaders(role, userId = 'user-1') {
    const token = await createSession({ userId, role, sessionId: `session-${userId}` }, { secret: SECRET });
    return { cookie: `actuate_session=${token}` };
}
describe('GET /api/cms/users/search', () => {
    const original = {
        CMS_SECRET: process.env.CMS_SECRET,
        CMS_SESSION_SECRET: process.env.CMS_SESSION_SECRET,
    };
    beforeEach(() => {
        process.env.CMS_SECRET = SECRET;
        delete process.env.CMS_SESSION_SECRET;
    });
    afterEach(() => {
        for (const [k, v] of Object.entries(original)) {
            if (v === undefined)
                delete process.env[k];
            else
                process.env[k] = v;
        }
    });
    function makeHandler(rows) {
        const db = createMockDB(rows);
        initDB(db);
        return handleActuateAPI({ prismaClient: db });
    }
    it('rejects unauthenticated requests with 401', async () => {
        const handler = makeHandler([]);
        const res = await handler(new Request('https://example.com/api/cms/users/search?q=alex'));
        expect(res.status).toBe(401);
    });
    it('returns matches by name (case-insensitive)', async () => {
        const handler = makeHandler([
            { id: 'u-1', name: 'Alice Anderson', email: 'alice@example.com', isActive: true },
            { id: 'u-2', name: 'Bob Brown', email: 'bob@example.com', isActive: true },
        ]);
        const headers = await authHeaders('EDITOR', 'me');
        const res = await handler(new Request('https://example.com/api/cms/users/search?q=ALICE', { headers }));
        expect(res.status).toBe(200);
        const body = (await res.json());
        expect(body.data.map((u) => u.id)).toEqual(['u-1']);
    });
    it('returns matches by email', async () => {
        const handler = makeHandler([
            { id: 'u-1', name: 'Alice', email: 'alice@example.com', isActive: true },
            { id: 'u-2', name: 'Bob', email: 'bob@example.com', isActive: true },
        ]);
        const headers = await authHeaders('VIEWER', 'me');
        const res = await handler(new Request('https://example.com/api/cms/users/search?q=bob%40', { headers }));
        const body = (await res.json());
        expect(body.data.map((u) => u.id)).toEqual(['u-2']);
    });
    it('omits unapproved users from the result set (PR #159 review)', async () => {
        const handler = makeHandler([
            { id: 'u-1', name: 'Alice', email: 'a@x.com', isActive: true, isApproved: true },
            { id: 'u-2', name: 'Alex', email: 'b@x.com', isActive: true, isApproved: false },
        ]);
        const headers = await authHeaders('EDITOR', 'me');
        const res = await handler(new Request('https://example.com/api/cms/users/search?q=al', { headers }));
        const body = (await res.json());
        expect(body.data.map((u) => u.id)).toEqual(['u-1']);
    });
    it('omits inactive users from the result set', async () => {
        const handler = makeHandler([
            { id: 'u-1', name: 'Alice', email: 'a@x.com', isActive: true },
            { id: 'u-2', name: 'Alex', email: 'b@x.com', isActive: false },
        ]);
        const headers = await authHeaders('EDITOR', 'me');
        const res = await handler(new Request('https://example.com/api/cms/users/search?q=al', { headers }));
        const body = (await res.json());
        expect(body.data.map((u) => u.id)).toEqual(['u-1']);
    });
    it('returns an empty array for an empty / whitespace query (no directory dump)', async () => {
        const handler = makeHandler([
            { id: 'u-1', name: 'Alice', email: 'a@x.com', isActive: true },
            { id: 'u-2', name: 'Bob', email: 'b@x.com', isActive: true },
        ]);
        const headers = await authHeaders('EDITOR', 'me');
        const empty = await handler(new Request('https://example.com/api/cms/users/search?q=', { headers }));
        const emptyBody = (await empty.json());
        expect(emptyBody.data).toEqual([]);
        const whitespace = await handler(new Request('https://example.com/api/cms/users/search?q=%20%20', { headers }));
        const wsBody = (await whitespace.json());
        expect(wsBody.data).toEqual([]);
    });
    it('does not expose role / hash / audit metadata in the result rows', async () => {
        const handler = makeHandler([{ id: 'u-1', name: 'Alice', email: 'a@x.com', isActive: true }]);
        const headers = await authHeaders('EDITOR', 'me');
        const res = await handler(new Request('https://example.com/api/cms/users/search?q=ali', { headers }));
        const body = (await res.json());
        expect(body.data[0]).toEqual({ id: 'u-1', name: 'Alice', email: 'a@x.com' });
        // Belt-and-braces — explicitly assert the sensitive keys are absent
        // even if the production select widens accidentally in the future.
        for (const k of ['role', 'passwordHash', 'isActive', 'createdAt', 'avatarUrl']) {
            expect(body.data[0]).not.toHaveProperty(k);
        }
    });
    it('caps the result set at 10 rows', async () => {
        const rows = Array.from({ length: 25 }, (_, i) => ({
            id: `u-${i}`,
            name: `User ${i}`,
            email: `u${i}@x.com`,
            isActive: true,
        }));
        const handler = makeHandler(rows);
        const headers = await authHeaders('EDITOR', 'me');
        const res = await handler(new Request('https://example.com/api/cms/users/search?q=user', { headers }));
        const body = (await res.json());
        expect(body.data).toHaveLength(10);
    });
    it('any authenticated role (including VIEWER) can query — mentions are not an admin tool', async () => {
        const handler = makeHandler([{ id: 'u-1', name: 'Alice', email: 'a@x.com', isActive: true }]);
        for (const role of ['VIEWER', 'EDITOR', 'ADMIN']) {
            const headers = await authHeaders(role, `${role}-me`);
            const res = await handler(new Request('https://example.com/api/cms/users/search?q=ali', { headers }));
            expect(res.status).toBe(200);
        }
    });
});
//# sourceMappingURL=users-search.test.js.map