import { hasChildren } from './types.js';
// ─── Block Purpose Classification ──────────────────────────────────────────
const BLOCK_PURPOSE = {
    hero: 'attention',
    video: 'attention',
    text: 'information',
    faq: 'information',
    image: 'information',
    gallery: 'information',
    code: 'information',
    cards: 'information',
    cta: 'conversion',
    form: 'conversion',
};
// ─── Internal Helpers ──────────────────────────────────────────────────────
function collectBlocks(node) {
    const blocks = [];
    if (node.type === 'block') {
        blocks.push(node);
    }
    if (hasChildren(node)) {
        for (const child of node.children) {
            blocks.push(...collectBlocks(child));
        }
    }
    return blocks;
}
function collectSections(tree) {
    return tree.children.filter((c) => c.type === 'section');
}
function countBlocksInNode(node) {
    if (node.type === 'block')
        return 1;
    if (!hasChildren(node))
        return 0;
    let count = 0;
    for (const child of node.children) {
        count += countBlocksInNode(child);
    }
    return count;
}
function collectRows(node) {
    const rows = [];
    if (node.type === 'row') {
        rows.push(node);
    }
    if (hasChildren(node)) {
        for (const child of node.children) {
            rows.push(...collectRows(child));
        }
    }
    return rows;
}
function parseSpacingValue(value) {
    if (!value)
        return null;
    const match = value.match(/^(\d+(?:\.\d+)?)/);
    return match ? parseFloat(match[1]) : null;
}
// ─── Category Scorers ──────────────────────────────────────────────────────
function scoreVisualHierarchy(tree, blocks) {
    const checks = [];
    let score = 20;
    const hasHero = blocks.some((b) => b.settings.blockType === 'hero');
    const sections = collectSections(tree);
    if (sections.length > 0 && hasHero) {
        const firstSectionBlocks = collectBlocks(sections[0]);
        const heroInFirst = firstSectionBlocks.some((b) => b.settings.blockType === 'hero');
        if (heroInFirst) {
            checks.push({
                id: 'hero-position',
                label: 'Hero placement',
                status: 'good',
                detail: 'Hero block is in the first section',
            });
        }
        else {
            checks.push({
                id: 'hero-position',
                label: 'Hero placement',
                status: 'warning',
                detail: 'Hero block is not in the first section',
            });
            score -= 5;
        }
    }
    else if (!hasHero && sections.length > 0) {
        checks.push({
            id: 'hero-missing',
            label: 'Hero block',
            status: 'warning',
            detail: 'No hero block found — consider adding one for visual impact',
        });
        score -= 5;
    }
    const headings = [];
    for (const block of blocks) {
        if (block.settings.blockType === 'hero') {
            headings.push({ level: 1, blockId: block.id });
        }
        if (block.settings.blockType === 'text' && block.data.headingLevel) {
            const level = parseInt(String(block.data.headingLevel).replace('h', ''), 10);
            if (!isNaN(level)) {
                headings.push({ level, blockId: block.id });
            }
        }
    }
    let headingFlowOk = true;
    for (let i = 1; i < headings.length; i++) {
        const prev = headings[i - 1].level;
        const curr = headings[i].level;
        if (curr > prev + 1) {
            headingFlowOk = false;
            break;
        }
    }
    if (headings.length > 0) {
        if (headingFlowOk) {
            checks.push({
                id: 'heading-flow',
                label: 'Heading hierarchy',
                status: 'good',
                detail: 'Headings follow a logical flow',
            });
        }
        else {
            checks.push({
                id: 'heading-flow',
                label: 'Heading hierarchy',
                status: 'warning',
                detail: 'Heading levels skip — may confuse readers and SEO',
            });
            score -= 5;
        }
    }
    const ctaBlocks = blocks.filter((b) => b.settings.blockType === 'cta' || b.settings.blockType === 'form');
    if (ctaBlocks.length > 0 && sections.length > 2) {
        const lastSectionBlocks = collectBlocks(sections[sections.length - 1]);
        const allCtaInLast = ctaBlocks.every((cta) => lastSectionBlocks.some((b) => b.id === cta.id));
        if (allCtaInLast && sections.length > 3) {
            checks.push({
                id: 'cta-position',
                label: 'CTA placement',
                status: 'warning',
                detail: 'All CTAs are in the last section — add one higher on the page',
            });
            score -= 5;
        }
        else {
            checks.push({
                id: 'cta-position',
                label: 'CTA placement',
                status: 'good',
                detail: 'CTA is well-positioned on the page',
            });
        }
    }
    return {
        name: 'visualHierarchy',
        label: 'Visual Hierarchy',
        score: Math.max(0, score),
        maxScore: 20,
        checks,
    };
}
function scoreSpacingConsistency(tree) {
    const checks = [];
    let score = 20;
    const sections = collectSections(tree);
    const paddings = [];
    for (const section of sections) {
        const top = parseSpacingValue(section.settings.paddingTop);
        const bottom = parseSpacingValue(section.settings.paddingBottom);
        if (top !== null)
            paddings.push(top);
        if (bottom !== null)
            paddings.push(bottom);
    }
    if (paddings.length >= 2) {
        const uniquePaddings = new Set(paddings);
        if (uniquePaddings.size <= 2) {
            checks.push({
                id: 'padding-consistency',
                label: 'Section padding',
                status: 'good',
                detail: 'Consistent padding across sections',
            });
        }
        else if (uniquePaddings.size <= 4) {
            checks.push({
                id: 'padding-consistency',
                label: 'Section padding',
                status: 'warning',
                detail: `${uniquePaddings.size} different padding values — consider standardizing`,
            });
            score -= 5;
        }
        else {
            checks.push({
                id: 'padding-consistency',
                label: 'Section padding',
                status: 'error',
                detail: `${uniquePaddings.size} different padding values — too inconsistent`,
            });
            score -= 10;
        }
    }
    else {
        checks.push({
            id: 'padding-consistency',
            label: 'Section padding',
            status: 'good',
            detail: 'Default padding used',
        });
    }
    const rows = collectRows(tree);
    const gaps = [];
    for (const row of rows) {
        const gap = parseSpacingValue(row.settings.gap);
        if (gap !== null)
            gaps.push(gap);
    }
    if (gaps.length >= 2) {
        const uniqueGaps = new Set(gaps);
        if (uniqueGaps.size <= 2) {
            checks.push({
                id: 'gap-consistency',
                label: 'Row gaps',
                status: 'good',
                detail: 'Consistent gap values',
            });
        }
        else {
            checks.push({
                id: 'gap-consistency',
                label: 'Row gaps',
                status: 'warning',
                detail: `${uniqueGaps.size} different gap values`,
            });
            score -= 5;
        }
    }
    else {
        checks.push({
            id: 'gap-consistency',
            label: 'Row gaps',
            status: 'good',
            detail: 'Default gaps used',
        });
    }
    return {
        name: 'spacingConsistency',
        label: 'Spacing Consistency',
        score: Math.max(0, score),
        maxScore: 20,
        checks,
    };
}
function scoreContentDensity(tree) {
    const checks = [];
    let score = 15;
    const sections = collectSections(tree);
    if (sections.length === 0) {
        checks.push({
            id: 'no-sections',
            label: 'Page content',
            status: 'error',
            detail: 'Page has no sections',
        });
        return { name: 'contentDensity', label: 'Content Density', score: 0, maxScore: 15, checks };
    }
    const blockCounts = sections.map((s) => countBlocksInNode(s));
    const overpackedSections = blockCounts.filter((c) => c > 5);
    const emptySections = blockCounts.filter((c) => c === 0);
    if (overpackedSections.length > 0) {
        checks.push({
            id: 'overpacked',
            label: 'Section density',
            status: 'warning',
            detail: `${overpackedSections.length} section(s) have more than 5 blocks — consider splitting`,
        });
        score -= 5;
    }
    else {
        checks.push({
            id: 'overpacked',
            label: 'Section density',
            status: 'good',
            detail: 'No sections are overpacked',
        });
    }
    if (emptySections.length > 0) {
        checks.push({
            id: 'empty-sections',
            label: 'Empty sections',
            status: 'warning',
            detail: `${emptySections.length} section(s) have no content`,
        });
        score -= 5;
    }
    if (blockCounts.length >= 3) {
        const max = Math.max(...blockCounts);
        const min = Math.min(...blockCounts.filter((c) => c > 0));
        if (max > 0 && min > 0 && max / min > 4) {
            checks.push({
                id: 'balance',
                label: 'Section balance',
                status: 'warning',
                detail: 'Large variation in section sizes — page may feel unbalanced',
            });
            score -= 3;
        }
        else {
            checks.push({
                id: 'balance',
                label: 'Section balance',
                status: 'good',
                detail: 'Sections are reasonably balanced',
            });
        }
    }
    return {
        name: 'contentDensity',
        label: 'Content Density',
        score: Math.max(0, score),
        maxScore: 15,
        checks,
    };
}
function scoreColorUsage(tree) {
    const checks = [];
    let score = 15;
    const sections = collectSections(tree);
    const backgrounds = sections.map((s) => s.settings.background);
    const definedBgs = backgrounds.filter((b) => !!b);
    const uniqueBgs = new Set(definedBgs);
    if (uniqueBgs.size <= 3) {
        checks.push({
            id: 'color-palette',
            label: 'Color palette',
            status: 'good',
            detail: `${uniqueBgs.size || 0} distinct background colors — restrained palette`,
        });
    }
    else if (uniqueBgs.size <= 5) {
        checks.push({
            id: 'color-palette',
            label: 'Color palette',
            status: 'warning',
            detail: `${uniqueBgs.size} distinct background colors — consider reducing`,
        });
        score -= 5;
    }
    else {
        checks.push({
            id: 'color-palette',
            label: 'Color palette',
            status: 'error',
            detail: `${uniqueBgs.size} distinct background colors — too many, feels inconsistent`,
        });
        score -= 10;
    }
    let adjacentSame = 0;
    for (let i = 1; i < sections.length; i++) {
        const prev = sections[i - 1].settings.background ?? 'default';
        const curr = sections[i].settings.background ?? 'default';
        if (prev === curr && prev !== 'default') {
            adjacentSame++;
        }
    }
    if (adjacentSame > 0) {
        checks.push({
            id: 'adjacent-bg',
            label: 'Visual separation',
            status: 'warning',
            detail: `${adjacentSame} adjacent section(s) share the same background — alternate for contrast`,
        });
        score -= 3;
    }
    else if (sections.length > 1) {
        checks.push({
            id: 'adjacent-bg',
            label: 'Visual separation',
            status: 'good',
            detail: 'Adjacent sections have distinct backgrounds',
        });
    }
    return {
        name: 'colorUsage',
        label: 'Color Usage',
        score: Math.max(0, score),
        maxScore: 15,
        checks,
    };
}
function scoreImageQuality(blocks) {
    const checks = [];
    let score = 15;
    const imageBlocks = blocks.filter((b) => b.settings.blockType === 'image' ||
        b.settings.blockType === 'hero' ||
        b.settings.blockType === 'gallery');
    if (imageBlocks.length === 0 && blocks.length > 3) {
        checks.push({
            id: 'no-images',
            label: 'Image usage',
            status: 'warning',
            detail: 'No images on the page — visual content improves engagement',
        });
        score -= 5;
    }
    else if (imageBlocks.length > 0) {
        checks.push({
            id: 'has-images',
            label: 'Image usage',
            status: 'good',
            detail: `${imageBlocks.length} image-containing block(s)`,
        });
    }
    let missingAlt = 0;
    for (const block of imageBlocks) {
        if (block.settings.blockType === 'image') {
            if (!block.data.alt || String(block.data.alt).trim() === '')
                missingAlt++;
        }
        if (block.settings.blockType === 'gallery' && Array.isArray(block.data.images)) {
            for (const img of block.data.images) {
                if (!img.alt || String(img.alt).trim() === '')
                    missingAlt++;
            }
        }
    }
    if (missingAlt > 0) {
        checks.push({
            id: 'alt-text',
            label: 'Image alt text',
            status: 'error',
            detail: `${missingAlt} image(s) missing alt text`,
        });
        score -= 7;
    }
    else if (imageBlocks.length > 0) {
        checks.push({
            id: 'alt-text',
            label: 'Image alt text',
            status: 'good',
            detail: 'All images have alt text',
        });
    }
    return {
        name: 'imageQuality',
        label: 'Image Quality',
        score: Math.max(0, score),
        maxScore: 15,
        checks,
    };
}
function scoreMobileReadiness(tree) {
    const checks = [];
    let score = 15;
    const rows = collectRows(tree);
    let mobileConsideredRows = 0;
    let multiColumnRows = 0;
    for (const row of rows) {
        if (row.children.length > 1) {
            multiColumnRows++;
            if (row.settings.wrapOnMobile || row.settings.reverseOnMobile) {
                mobileConsideredRows++;
            }
        }
    }
    if (multiColumnRows > 0) {
        if (mobileConsideredRows === multiColumnRows) {
            checks.push({
                id: 'mobile-wrap',
                label: 'Mobile column behavior',
                status: 'good',
                detail: 'All multi-column rows have mobile settings',
            });
        }
        else if (mobileConsideredRows > 0) {
            checks.push({
                id: 'mobile-wrap',
                label: 'Mobile column behavior',
                status: 'warning',
                detail: `${multiColumnRows - mobileConsideredRows} multi-column row(s) lack mobile wrap/reverse settings`,
            });
            score -= 5;
        }
        else {
            checks.push({
                id: 'mobile-wrap',
                label: 'Mobile column behavior',
                status: 'warning',
                detail: 'No multi-column rows have mobile-specific settings',
            });
            score -= 7;
        }
    }
    else {
        checks.push({
            id: 'mobile-wrap',
            label: 'Mobile layout',
            status: 'good',
            detail: 'Single-column layout — mobile-friendly by default',
        });
    }
    const narrowColumns = rows.flatMap((r) => r.children.filter((c) => c.settings.width <= 2));
    if (narrowColumns.length > 0) {
        checks.push({
            id: 'narrow-cols',
            label: 'Narrow columns',
            status: 'warning',
            detail: `${narrowColumns.length} column(s) are 2-units or narrower — may be hard to read on mobile`,
        });
        score -= 3;
    }
    return {
        name: 'mobileReadiness',
        label: 'Mobile Readiness',
        score: Math.max(0, score),
        maxScore: 15,
        checks,
    };
}
// ─── Semantic Suggestions ──────────────────────────────────────────────────
function generateSuggestions(tree, blocks) {
    const suggestions = [];
    const sections = collectSections(tree);
    const purposes = new Set(blocks.map((b) => BLOCK_PURPOSE[b.settings.blockType] ?? 'information'));
    if (!purposes.has('conversion')) {
        suggestions.push({
            id: 'missing-cta',
            message: 'No conversion element found — add a CTA or form to drive user action',
            severity: 'important',
            category: 'conversion',
        });
    }
    const hasTrustElement = blocks.some((b) => {
        if (b.settings.blockType === 'cards' && b.settings.variant?.includes('testimonial'))
            return true;
        if (b.settings.blockType === 'gallery')
            return true;
        return false;
    });
    if (!hasTrustElement && blocks.length > 3) {
        suggestions.push({
            id: 'missing-trust',
            message: 'No trust element — consider adding testimonials, client logos, or a gallery',
            severity: 'suggestion',
            category: 'trust',
        });
    }
    let consecutiveInfo = 0;
    let maxConsecutiveInfo = 0;
    for (const section of sections) {
        const sectionBlocks = collectBlocks(section);
        const allInfo = sectionBlocks.every((b) => (BLOCK_PURPOSE[b.settings.blockType] ?? 'information') === 'information');
        if (allInfo && sectionBlocks.length > 0) {
            consecutiveInfo++;
            maxConsecutiveInfo = Math.max(maxConsecutiveInfo, consecutiveInfo);
        }
        else {
            consecutiveInfo = 0;
        }
    }
    if (maxConsecutiveInfo >= 3) {
        suggestions.push({
            id: 'info-streak',
            message: `${maxConsecutiveInfo} information sections in a row — add a visual break (image, gallery, or CTA)`,
            severity: 'suggestion',
            category: 'structure',
        });
    }
    const hasFaq = blocks.some((b) => b.settings.blockType === 'faq');
    if (!hasFaq && sections.length >= 4) {
        suggestions.push({
            id: 'suggest-faq',
            message: 'Consider adding an FAQ section — helps with SEO and user questions',
            severity: 'info',
            category: 'information',
        });
    }
    if (sections.length === 0) {
        suggestions.push({
            id: 'empty-page',
            message: 'Page has no content — add sections to build your page',
            severity: 'important',
            category: 'structure',
        });
    }
    return suggestions;
}
// ─── Main Export ────────────────────────────────────────────────────────────
export function analyzeDesign(tree) {
    const blocks = collectBlocks(tree);
    const categories = [
        scoreVisualHierarchy(tree, blocks),
        scoreSpacingConsistency(tree),
        scoreContentDensity(tree),
        scoreColorUsage(tree),
        scoreImageQuality(blocks),
        scoreMobileReadiness(tree),
    ];
    const totalScore = categories.reduce((sum, c) => sum + c.score, 0);
    const suggestions = generateSuggestions(tree, blocks);
    return {
        score: totalScore,
        categories,
        suggestions,
    };
}
//# sourceMappingURL=design-scorer.js.map