import { describe, expect, it, vi, beforeEach } from 'vitest';
const dbHolder = { current: {} };
vi.mock('../../db.js', () => ({ getDB: () => dbHolder.current }));
vi.mock('../../security/audit.js', () => ({ logEvent: vi.fn(async () => { }) }));
vi.mock('../../config/runtime.js', () => ({
    getActuateConfig: () => ({ forms: { defaultSuccessMessage: 'Thanks!' } }),
}));
import { processSubmission, buildPublicForm, SubmissionValidationError, } from '../../forms/submission.js';
function field(overrides) {
    return {
        id: 'f',
        label: 'Label',
        key: 'key',
        type: 'text',
        required: false,
        sortOrder: 0,
        ...overrides,
    };
}
function form(overrides = {}) {
    return {
        id: 'form_1',
        name: 'Contact Us',
        slug: 'contact-us',
        status: 'active',
        activeSchemaVersionId: null,
        fields: [
            field({ key: 'name', type: 'text', required: true }),
            field({ key: 'email', type: 'email', required: true }),
            field({ key: 'message', type: 'textarea' }),
        ],
        successMessage: 'Got it!',
        ...overrides,
    };
}
function fakeDb() {
    const created = [];
    return {
        created,
        $transaction: async (fn) => fn({
            formSubmission: {
                aggregate: async () => ({ _max: { entryNumber: 4 } }),
                create: async ({ data }) => {
                    created.push(data);
                    return { id: 'sub_1', entryNumber: data.entryNumber };
                },
            },
        }),
    };
}
describe('processSubmission', () => {
    beforeEach(() => {
        dbHolder.current = fakeDb();
    });
    it('persists a clean submission with the next entry number and derived sender', async () => {
        const result = await processSubmission(form(), {
            values: { name: 'Ada Lovelace', email: 'ada@calc.io', message: 'Hello there' },
            ip: '203.0.113.5',
            userAgent: 'jest',
            attribution: { source: 'google', medium: 'cpc', campaign: 'spring' },
        });
        expect(result.spamStatus).toBe('clean');
        expect(result.entryNumber).toBe(5);
        expect(result.confirmation).toEqual({ type: 'message', message: 'Got it!' });
        const row = dbHolder.current.created[0];
        expect(row.senderName).toBe('Ada Lovelace');
        expect(row.senderEmail).toBe('ada@calc.io');
        expect(row.spamStatus).toBe('clean');
        expect(row.utmSource).toBe('google');
        expect(row.utmCampaign).toBe('spring');
        // IP is hashed, never stored raw.
        expect(row.ipAddressHash).toBeTruthy();
        expect(row.ipAddressHash).not.toContain('203.0.113.5');
    });
    it('marks a honeypot-tripped submission as spam but still persists it', async () => {
        const result = await processSubmission(form({ fields: [...form().fields, field({ key: 'company', type: 'honeypot' })] }), {
            values: { name: 'Bot', email: 'bot@spam.io', company: 'filled-by-bot' },
        });
        expect(result.spamStatus).toBe('spam');
        expect(dbHolder.current.created[0].spamStatus).toBe('spam');
    });
    it('throws SubmissionValidationError on invalid input', async () => {
        await expect(processSubmission(form(), { values: { name: '', email: 'nope' } })).rejects.toBeInstanceOf(SubmissionValidationError);
    });
    it('builds a redirect confirmation when the form has a redirect URL', async () => {
        const result = await processSubmission(form({ redirectUrl: 'https://site.com/thanks' }), {
            values: { name: 'Ada', email: 'ada@calc.io' },
        });
        expect(result.confirmation).toEqual({
            type: 'redirect',
            redirectUrl: 'https://site.com/thanks',
        });
    });
});
describe('buildPublicForm', () => {
    it('projects a safe, embeddable schema', () => {
        const f = form({
            fields: [
                field({
                    key: 'name',
                    type: 'text',
                    validationRules: { required: true, customValidator: 'secretHook' },
                }),
                field({ key: 'hp', type: 'honeypot' }),
            ],
            spamSettings: { honeypotFieldKey: 'hp', minSubmitSeconds: 3, blockedEmails: ['x@y.com'] },
        });
        const publicForm = buildPublicForm(f, f.fields, null, {
            captcha: { provider: 'turnstile', siteKey: 'site-123' },
        });
        // Honeypot field is never exposed.
        expect(publicForm.fields.find((field) => field.key === 'hp')).toBeUndefined();
        // Server-only validator name is stripped.
        expect(publicForm.fields[0]?.validationRules).not.toHaveProperty('customValidator');
        // Spam internals limited to render-time hints; blocklist never leaks.
        expect(publicForm.spam.honeypotFieldKey).toBe('hp');
        expect(publicForm.spam.minSubmitSeconds).toBe(3);
        expect(JSON.stringify(publicForm)).not.toContain('x@y.com');
        // Submit path + captcha hint exposed.
        expect(publicForm.submitPath).toBe('/api/cms/public/forms/contact-us/submit');
        expect(publicForm.captcha).toEqual({ provider: 'turnstile', siteKey: 'site-123' });
    });
    it('omits the captcha hint when no provider is configured', () => {
        const f = form();
        const publicForm = buildPublicForm(f, f.fields, null, {
            captcha: { provider: 'none', siteKey: '' },
        });
        expect(publicForm.captcha).toBeUndefined();
    });
});
//# sourceMappingURL=submission-pipeline.test.js.map