import { describe, it, expect, vi, beforeEach } from 'vitest';
import { migratePageBuilderSections } from '../../sections/migrate.js';
/**
 * Mock Prisma supporting the keyset scan used by the migration plus the
 * read/transaction surface `updateDocument` needs.
 */
function createMockDB() {
    const docs = new Map();
    const versions = [];
    function sortedNonDeleted() {
        return [...docs.values()]
            .filter((d) => d.deletedAt === null)
            .sort((a, b) => (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
    }
    const txClient = {
        document: {
            update: vi.fn(async ({ where, data: updateData }) => {
                const doc = docs.get(where.id);
                if (!doc)
                    throw new Error('Not found');
                Object.assign(doc, updateData);
                return doc;
            }),
        },
        version: {
            create: vi.fn(async ({ data }) => {
                const v = { id: `ver-${versions.length}`, ...data, createdAt: new Date() };
                versions.push(v);
                return v;
            }),
        },
    };
    return {
        document: {
            findMany: vi.fn(async ({ take, skip, cursor, select } = {}) => {
                let arr = sortedNonDeleted();
                if (cursor?.id) {
                    const idx = arr.findIndex((d) => d.id === cursor.id);
                    if (idx >= 0)
                        arr = arr.slice(idx + (skip ?? 0));
                }
                if (typeof take === 'number')
                    arr = arr.slice(0, take);
                if (select) {
                    arr = arr.map((d) => {
                        const picked = {};
                        for (const k of Object.keys(select))
                            picked[k] = d[k];
                        return picked;
                    });
                }
                return arr;
            }),
            findFirst: vi.fn(async ({ where }) => {
                for (const doc of docs.values()) {
                    if (where.id && doc.id !== where.id)
                        continue;
                    if (where.collection && doc.collection !== where.collection)
                        continue;
                    if (where.deletedAt === null && doc.deletedAt !== null)
                        continue;
                    return doc;
                }
                return null;
            }),
        },
        version: txClient.version,
        $transaction: vi.fn(async (fn) => fn(txClient)),
        _docs: docs,
        _versions: versions,
    };
}
function ctxFor(db) {
    return { userId: 'admin-1', role: 'ADMIN', db };
}
const HERO_LAYOUT = {
    type: 'page',
    children: [
        {
            type: 'section',
            settings: { htmlId: 'top' },
            children: [
                { type: 'block', settings: { blockType: 'hero' }, data: { title: 'Hi', subtitle: 'Yo' } },
            ],
        },
    ],
};
function seedDoc(db, id, data) {
    db._docs.set(id, { id, collection: 'pages', status: 'PUBLISHED', data, deletedAt: null });
}
describe('migratePageBuilderSections (ADR 0002 backfill)', () => {
    let db;
    beforeEach(() => {
        db = createMockDB();
    });
    it('backfills sections for a legacy _layout-only document', async () => {
        seedDoc(db, 'doc-1', { title: 'Legacy', _layout: HERO_LAYOUT });
        const result = await migratePageBuilderSections(ctxFor(db));
        expect(result.scanned).toBe(1);
        expect(result.migrated).toBe(1);
        expect(result.migratedIds).toEqual(['doc-1']);
        const sections = db._docs.get('doc-1').data.sections;
        expect(sections[0].sectionType).toBe('hero');
        expect(sections[0].content.heading).toBe('Hi');
    });
    it('is idempotent — a second run migrates nothing', async () => {
        seedDoc(db, 'doc-1', { title: 'Legacy', _layout: HERO_LAYOUT });
        await migratePageBuilderSections(ctxFor(db));
        const second = await migratePageBuilderSections(ctxFor(db));
        expect(second.migrated).toBe(0);
        expect(second.skipped).toBe(1);
    });
    it('skips documents that already have sections, and those with no _layout', async () => {
        seedDoc(db, 'doc-1', { title: 'Already', _layout: HERO_LAYOUT, sections: [{ id: 'x' }] });
        seedDoc(db, 'doc-2', { title: 'Flat', sections: [{ id: 'y' }] });
        seedDoc(db, 'doc-3', { title: 'Bare' });
        const result = await migratePageBuilderSections(ctxFor(db));
        expect(result.scanned).toBe(3);
        expect(result.migrated).toBe(0);
        expect(result.skipped).toBe(3);
    });
    it('skips a layout that compiles to nothing (leaves it for the read-time bridge)', async () => {
        seedDoc(db, 'doc-1', { title: 'Empty', _layout: { type: 'page', children: [] } });
        const result = await migratePageBuilderSections(ctxFor(db));
        expect(result.migrated).toBe(0);
        expect(result.skipped).toBe(1);
        expect(db._docs.get('doc-1').data.sections).toBeUndefined();
    });
    it('dryRun reports counts without writing', async () => {
        seedDoc(db, 'doc-1', { title: 'Legacy', _layout: HERO_LAYOUT });
        const result = await migratePageBuilderSections(ctxFor(db), { dryRun: true });
        expect(result.migrated).toBe(1);
        expect(db._docs.get('doc-1').data.sections).toBeUndefined();
    });
    it('pages through batches (batchSize smaller than the doc count)', async () => {
        seedDoc(db, 'doc-1', { title: 'A', _layout: HERO_LAYOUT });
        seedDoc(db, 'doc-2', { title: 'B', _layout: HERO_LAYOUT });
        seedDoc(db, 'doc-3', { title: 'C', _layout: HERO_LAYOUT });
        const result = await migratePageBuilderSections(ctxFor(db), { batchSize: 1 });
        expect(result.scanned).toBe(3);
        expect(result.migrated).toBe(3);
        expect(result.migratedIds.sort()).toEqual(['doc-1', 'doc-2', 'doc-3']);
    });
});
//# sourceMappingURL=sections-migrate.test.js.map