import { describe, expect, it } from 'vitest';
import { addSection, createSection, duplicateSection, emptyPage, moveSection, removeSection, reorderSections, toggleSectionVisibility, updateSectionContent, updateSectionMeta, updateSectionSettings, validatePage, } from '../../lib/page-editor-service.js';
import { SECTION_TYPES, SECTION_TYPE_LIST } from '../../views/page-editor/section-types.js';
function basePage(sections) {
    return { ...emptyPage(), title: 'About', slug: 'about', path: '/about', sections };
}
describe('createSection', () => {
    it('seeds content + settings from the registry and a unique id', () => {
        const a = createSection('hero');
        const b = createSection('hero');
        expect(a.sectionType).toBe('hero');
        expect(a.name).toBe(SECTION_TYPES.hero.name);
        expect(a.visible).toBe(true);
        expect(a.id).not.toBe(b.id);
        expect(a.content.heading).toBe(SECTION_TYPES.hero.defaultContent.heading);
    });
    it('deep-clones defaults so sections never share references', () => {
        const a = createSection('stats');
        const b = createSection('stats');
        a.content.stats.push({ value: 'x' });
        expect(b.content.stats.length).toBe(SECTION_TYPES.stats.defaultContent.stats.length);
    });
});
describe('section array helpers', () => {
    const s1 = createSection('hero');
    const s2 = createSection('text');
    const s3 = createSection('cta');
    const list = [s1, s2, s3];
    it('addSection appends by default and inserts at an index', () => {
        const extra = createSection('stats');
        expect(addSection(list, extra).map((s) => s.id)).toEqual([s1.id, s2.id, s3.id, extra.id]);
        expect(addSection(list, extra, 1).map((s) => s.id)).toEqual([s1.id, extra.id, s2.id, s3.id]);
    });
    it('removeSection drops the matching id', () => {
        expect(removeSection(list, s2.id).map((s) => s.id)).toEqual([s1.id, s3.id]);
    });
    it('moveSection relocates and clamps', () => {
        expect(moveSection(list, s1.id, 2).map((s) => s.id)).toEqual([s2.id, s3.id, s1.id]);
        expect(moveSection(list, s1.id, 99).map((s) => s.id)).toEqual([s2.id, s3.id, s1.id]);
    });
    it('reorderSections respects explicit order and never drops sections', () => {
        expect(reorderSections(list, [s3.id, s1.id, s2.id]).map((s) => s.id)).toEqual([
            s3.id,
            s1.id,
            s2.id,
        ]);
        // missing ids are appended defensively rather than lost
        expect(reorderSections(list, [s2.id]).map((s) => s.id)).toEqual([s2.id, s1.id, s3.id]);
    });
    it('duplicateSection inserts a new-id copy right after the original', () => {
        const out = duplicateSection(list, s2.id);
        expect(out).toHaveLength(4);
        expect(out[2].id).not.toBe(s2.id);
        expect(out[2].name).toBe(`${s2.name} (Copy)`);
        expect(out[2].sectionType).toBe(s2.sectionType);
    });
    it('toggleSectionVisibility flips only the target', () => {
        const out = toggleSectionVisibility(list, s1.id);
        expect(out.find((s) => s.id === s1.id).visible).toBe(false);
        expect(out.find((s) => s.id === s2.id).visible).toBe(true);
    });
    it('updateSectionContent / settings / meta patch immutably', () => {
        const c = updateSectionContent(list, s1.id, { heading: 'New' });
        expect(c.find((s) => s.id === s1.id).content.heading).toBe('New');
        expect(list.find((s) => s.id === s1.id).content.heading).not.toBe('New');
        const st = updateSectionSettings(list, s1.id, { paddingY: 'sm' });
        expect(st.find((s) => s.id === s1.id).settings.paddingY).toBe('sm');
        const m = updateSectionMeta(list, s1.id, { name: 'Renamed' });
        expect(m.find((s) => s.id === s1.id).name).toBe('Renamed');
    });
});
describe('validatePage', () => {
    it('requires title and path', () => {
        const page = basePage([]);
        page.title = '';
        page.path = '';
        const { errors } = validatePage(page, false);
        expect(errors).toContain('Page title is required.');
        expect(errors).toContain('Page path is required.');
    });
    it('requires at least one visible section to publish', () => {
        const hidden = { ...createSection('hero'), visible: false };
        const { errors } = validatePage(basePage([hidden]), true);
        expect(errors.some((e) => e.includes('at least one visible section'))).toBe(true);
    });
    it('flags missing required section fields on publish', () => {
        const hero = createSection('hero');
        hero.content.heading = ''; // required
        const { errors } = validatePage(basePage([hero]), true);
        expect(errors.some((e) => e.includes('Heading'))).toBe(true);
    });
    it('flags invalid button URLs and missing image alt on publish', () => {
        const feature = createSection('feature');
        feature.content.heading = 'Has heading';
        feature.content.buttonLabel = 'Learn more';
        feature.content.buttonUrl = 'not a url';
        feature.content.imageUrl = 'https://x/img.png';
        feature.content.imageAlt = '';
        const { errors } = validatePage(basePage([feature]), true);
        expect(errors.some((e) => e.includes('not a valid URL'))).toBe(true);
        expect(errors.some((e) => e.includes('alt text'))).toBe(true);
    });
    it('accepts a valid page and warns on missing SEO', () => {
        const hero = createSection('hero');
        const { errors, warnings } = validatePage(basePage([hero]), true);
        expect(errors).toEqual([]);
        expect(warnings.length).toBeGreaterThan(0);
    });
    it('does not run publish-only rules for draft saves', () => {
        const { errors } = validatePage(basePage([]), false);
        expect(errors.some((e) => e.includes('visible section'))).toBe(false);
    });
    it('rejects an invalid slug (mirrors the server publish gate)', () => {
        const page = basePage([]);
        page.slug = 'About Us!';
        const { errors } = validatePage(page, false);
        expect(errors.some((e) => /slug/i.test(e))).toBe(true);
    });
    it('rejects a reserved slug', () => {
        const page = basePage([]);
        page.slug = 'admin';
        const { errors } = validatePage(page, false);
        expect(errors.some((e) => /reserved/i.test(e))).toBe(true);
    });
});
describe('section type registry', () => {
    it('every listed type has a renderable id, defaults, fields, and a scope', () => {
        const renderable = new Set([
            'hero',
            'text',
            'feature',
            'stats',
            'cta',
            'form',
            'article-body',
            'quote',
            'author-bio',
            'related-posts',
            'gallery',
            'embed',
        ]);
        expect(SECTION_TYPE_LIST.length).toBe(renderable.size);
        for (const def of SECTION_TYPE_LIST) {
            expect(renderable.has(def.id)).toBe(true);
            expect(def.fields.length).toBeGreaterThan(0);
            expect(def.defaultContent).toBeTruthy();
            expect(def.scope.length).toBeGreaterThan(0);
        }
    });
});
//# sourceMappingURL=page-editor-service.test.js.map