import { describe, expect, it, vi, beforeEach } from 'vitest';
// Mutable fake Prisma client swapped per test.
const dbHolder = { current: {} };
vi.mock('../../db.js', () => ({ getDB: () => dbHolder.current }));
// Audit logging writes to the DB; no-op it so service logic is isolated.
vi.mock('../../security/audit.js', () => ({ logEvent: vi.fn() }));
import { validateFormSchema, parseFormDefinition } from '../../forms/service.js';
import { listEntries, getEntryCounts, normalizeEntry } from '../../forms/entries.js';
function field(overrides) {
    return {
        id: 'f1',
        label: 'Label',
        key: 'key',
        type: 'text',
        required: false,
        sortOrder: 0,
        ...overrides,
    };
}
describe('validateFormSchema', () => {
    it('accepts a valid field set', () => {
        const result = validateFormSchema([
            field({ key: 'name', label: 'Name', type: 'text' }),
            field({ key: 'email', label: 'Email', type: 'email' }),
        ]);
        expect(result.valid).toBe(true);
        expect(result.errors).toHaveLength(0);
    });
    it('rejects duplicate field keys', () => {
        const result = validateFormSchema([field({ key: 'name' }), field({ key: 'name', id: 'f2' })]);
        expect(result.valid).toBe(false);
        expect(result.errors.some((e) => e.includes('Duplicate field key'))).toBe(true);
    });
    it('rejects invalid key syntax', () => {
        const result = validateFormSchema([field({ key: '123-bad' })]);
        expect(result.valid).toBe(false);
        expect(result.errors.some((e) => e.includes('Invalid field key'))).toBe(true);
    });
    it('requires a label', () => {
        const result = validateFormSchema([field({ key: 'ok', label: '' })]);
        expect(result.valid).toBe(false);
        expect(result.errors.some((e) => e.includes('missing a label'))).toBe(true);
    });
    it('rejects unsupported field types', () => {
        const result = validateFormSchema([field({ key: 'ok', type: 'magic' })]);
        expect(result.valid).toBe(false);
        expect(result.errors.some((e) => e.includes('is not allowed'))).toBe(true);
    });
    it('requires options for select/radio fields', () => {
        const result = validateFormSchema([field({ key: 'choice', type: 'select', options: [] })]);
        expect(result.valid).toBe(false);
        expect(result.errors.some((e) => e.includes('requires at least one option'))).toBe(true);
    });
    it('rejects a non-array payload', () => {
        const result = validateFormSchema({ not: 'an array' });
        expect(result.valid).toBe(false);
    });
});
describe('parseFormDefinition', () => {
    it('parses status, fields, and notify flag from document data', () => {
        const def = parseFormDefinition({
            id: 'form_1',
            title: 'ignored',
            slug: 'ignored',
            createdById: 'user_1',
            createdAt: new Date('2026-01-01T00:00:00Z'),
            updatedAt: new Date('2026-01-02T00:00:00Z'),
            data: {
                name: 'Contact Us',
                slug: 'contact-us',
                description: 'General contact',
                status: 'active',
                fields: [field({ key: 'name' })],
                notificationSettings: { enabled: true, recipients: ['a@b.co'] },
            },
        });
        expect(def.name).toBe('Contact Us');
        expect(def.slug).toBe('contact-us');
        expect(def.status).toBe('active');
        expect(def.fields).toHaveLength(1);
        expect(def.notifyEnabled).toBe(true);
        expect(def.createdBy).toBe('user_1');
    });
    it('defaults unknown status to draft', () => {
        const def = parseFormDefinition({ id: 'x', data: { name: 'X', status: 'weird' } });
        expect(def.status).toBe('draft');
    });
});
describe('normalizeEntry', () => {
    it('prefers persisted sender columns', () => {
        const entry = normalizeEntry({
            id: 's1',
            formId: 'f1',
            data: { name: 'ignored' },
            senderName: 'Marcus Webb',
            senderEmail: 'marcus@weave.com',
            preview: 'Hello there',
            unread: true,
            starred: false,
            archived: false,
            spamStatus: 'clean',
            submittedAt: new Date('2026-05-24T09:14:00Z'),
            createdAt: new Date('2026-05-24T09:14:00Z'),
            updatedAt: new Date('2026-05-24T09:14:00Z'),
        }, { name: 'Contact Us', slug: 'contact-us' });
        expect(entry.senderName).toBe('Marcus Webb');
        expect(entry.senderEmail).toBe('marcus@weave.com');
        expect(entry.formName).toBe('Contact Us');
        expect(entry.unread).toBe(true);
    });
    it('derives sender/preview from data when columns are absent (legacy rows)', () => {
        const entry = normalizeEntry({
            id: 's2',
            formId: 'f1',
            data: { name: 'Priya Nair', email: 'p@nair.io', message: 'We are launching a SaaS product.' },
            unread: false,
            starred: false,
            archived: false,
            spamStatus: 'clean',
            submittedAt: new Date(),
            createdAt: new Date(),
            updatedAt: new Date(),
        });
        expect(entry.senderName).toBe('Priya Nair');
        expect(entry.senderEmail).toBe('p@nair.io');
        expect(entry.preview).toContain('launching a SaaS');
    });
});
describe('listEntries where-builder', () => {
    let recordedWhere;
    function fakeDb(rows) {
        return {
            formSubmission: {
                findMany: vi.fn(async (args) => {
                    recordedWhere = args.where;
                    return rows;
                }),
                count: vi.fn(async () => rows.length),
            },
            document: { findMany: vi.fn(async () => []) },
        };
    }
    beforeEach(() => {
        recordedWhere = undefined;
    });
    it('excludes archived and spam by default', async () => {
        dbHolder.current = fakeDb([]);
        await listEntries({});
        expect(recordedWhere.archived).toBe(false);
        expect(recordedWhere.spamStatus).toEqual({ not: 'spam' });
    });
    it('filters to unread/starred when requested', async () => {
        dbHolder.current = fakeDb([]);
        await listEntries({ unread: true, starred: true });
        expect(recordedWhere.unread).toBe(true);
        expect(recordedWhere.starred).toBe(true);
    });
    it('builds a case-insensitive OR search across sender fields', async () => {
        dbHolder.current = fakeDb([]);
        await listEntries({ search: 'marcus' });
        expect(Array.isArray(recordedWhere.OR)).toBe(true);
        expect(recordedWhere.OR[0]).toEqual({ senderName: { contains: 'marcus', mode: 'insensitive' } });
    });
    it('scopes by formId and includes archived when explicitly asked', async () => {
        dbHolder.current = fakeDb([]);
        await listEntries({ formId: 'f1', archived: true });
        expect(recordedWhere.formId).toBe('f1');
        expect(recordedWhere.archived).toBe(true);
    });
});
describe('getEntryCounts', () => {
    it('returns chip totals and per-form unread map', async () => {
        const count = vi
            .fn()
            .mockResolvedValueOnce(18) // total
            .mockResolvedValueOnce(5) // unread
            .mockResolvedValueOnce(3) // starred
            .mockResolvedValueOnce(15); // this week
        dbHolder.current = {
            formSubmission: {
                count,
                groupBy: vi.fn(async () => [{ formId: 'f1', _count: { _all: 8 } }]),
            },
        };
        const counts = await getEntryCounts({});
        expect(counts.total).toBe(18);
        expect(counts.unread).toBe(5);
        expect(counts.starred).toBe(3);
        expect(counts.thisWeek).toBe(15);
        expect(counts.byForm.f1).toBe(8);
    });
});
//# sourceMappingURL=forms-service.test.js.map