import { describe, it, expect } from 'vitest';
import { calculatePageSeoScore, calculateSiteSeoScore, gradeForSeoScore, countH1, PAGE_SEO_WEIGHTS, SITE_SEO_WEIGHTS, } from '../../seo/score.js';
const FULL_PAGE = {
    title: 'Best Practices Guide',
    slug: 'best-practices',
    url: '/best-practices',
    content: '<h1>Best Practices</h1>' + 'word '.repeat(400),
    metaTitle: 'Best Practices Guide for Modern Teams in 2026',
    metaDescription: 'A comprehensive, practical guide to modern best practices covering process, tooling, and culture so your team can ship reliably and confidently every single week.',
    focusKeyword: 'best practices',
    canonicalUrl: 'https://example.com/best-practices',
    ogTitle: 'Best Practices Guide',
    ogDescription: 'Practical guide.',
    ogImage: 'https://example.com/og.png',
    structuredDataType: 'Article',
    noindex: false,
    shouldBeIndexable: true,
    brokenInternalLinks: 0,
};
describe('weights', () => {
    it('page weights sum to 100', () => {
        const sum = Object.values(PAGE_SEO_WEIGHTS).reduce((a, b) => a + b, 0);
        expect(sum).toBe(100);
    });
    it('site weights sum to 100', () => {
        const sum = Object.values(SITE_SEO_WEIGHTS).reduce((a, b) => a + b, 0);
        expect(sum).toBe(100);
    });
});
describe('gradeForSeoScore', () => {
    it('maps score bands to grades', () => {
        expect(gradeForSeoScore(95)).toBe('good');
        expect(gradeForSeoScore(80)).toBe('good');
        expect(gradeForSeoScore(74)).toBe('fair');
        expect(gradeForSeoScore(60)).toBe('fair');
        expect(gradeForSeoScore(45)).toBe('poor');
        expect(gradeForSeoScore(1)).toBe('poor');
        expect(gradeForSeoScore(0)).toBe('critical');
    });
});
describe('countH1', () => {
    it('counts h1 tags case-insensitively', () => {
        expect(countH1('<h1>a</h1>')).toBe(1);
        expect(countH1('<H1 class="x">a</H1><h1>b</h1>')).toBe(2);
        expect(countH1('<h2>no</h2>')).toBe(0);
    });
});
describe('calculatePageSeoScore', () => {
    it('scores a fully-optimised page near 100', () => {
        const result = calculatePageSeoScore(FULL_PAGE);
        expect(result.score).toBeGreaterThanOrEqual(95);
        expect(result.grade).toBe('good');
        expect(result.checks.every((c) => c.points <= c.max)).toBe(true);
    });
    it('scores an empty page as critical', () => {
        const result = calculatePageSeoScore({});
        expect(result.score).toBeLessThan(40);
        expect(['poor', 'critical']).toContain(result.grade);
    });
    it('fails the meta title check when missing', () => {
        const result = calculatePageSeoScore({ ...FULL_PAGE, metaTitle: '' });
        const check = result.checks.find((c) => c.id === 'meta-title');
        expect(check?.status).toBe('fail');
        expect(check?.points).toBe(0);
    });
    it('fails meta description and lowers the score', () => {
        const withDesc = calculatePageSeoScore(FULL_PAGE).score;
        const noDesc = calculatePageSeoScore({ ...FULL_PAGE, metaDescription: '' }).score;
        expect(noDesc).toBeLessThan(withDesc);
        const check = calculatePageSeoScore({ ...FULL_PAGE, metaDescription: '' }).checks.find((c) => c.id === 'meta-description');
        expect(check?.status).toBe('fail');
    });
    it('flags accidental noindex on indexable content', () => {
        const result = calculatePageSeoScore({ ...FULL_PAGE, noindex: true, shouldBeIndexable: true });
        const check = result.checks.find((c) => c.id === 'indexable');
        expect(check?.status).toBe('fail');
    });
    it('does not penalise noindex when content should not be indexable', () => {
        const result = calculatePageSeoScore({ ...FULL_PAGE, noindex: true, shouldBeIndexable: false });
        const check = result.checks.find((c) => c.id === 'indexable');
        expect(check?.status).toBe('pass');
    });
    it('fails the H1 check when there are zero H1 tags in non-empty content', () => {
        const result = calculatePageSeoScore({ ...FULL_PAGE, content: '<p>no heading</p>' });
        const check = result.checks.find((c) => c.id === 'h1');
        expect(check?.status).toBe('fail');
    });
    it('warns on multiple H1 tags', () => {
        const result = calculatePageSeoScore({ ...FULL_PAGE, content: '<h1>a</h1><h1>b</h1>' });
        const check = result.checks.find((c) => c.id === 'h1');
        expect(check?.status).toBe('warn');
    });
    it('flags broken internal links', () => {
        const result = calculatePageSeoScore({ ...FULL_PAGE, brokenInternalLinks: 3 });
        const check = result.checks.find((c) => c.id === 'internal-links');
        expect(check?.status).toBe('fail');
        expect(check?.message).toContain('3');
    });
    it('uses provided wordCount over content', () => {
        const result = calculatePageSeoScore({ ...FULL_PAGE, content: '<h1>x</h1>', wordCount: 500 });
        const check = result.checks.find((c) => c.id === 'content-length');
        expect(check?.status).toBe('pass');
    });
});
describe('calculateSiteSeoScore', () => {
    it('returns 100 when all categories are perfect', () => {
        const result = calculateSiteSeoScore({
            content: 100,
            technical: 100,
            linkRedirect: 100,
            structuredSocial: 100,
            performance: 100,
        });
        expect(result.score).toBe(100);
        expect(result.grade).toBe('good');
    });
    it('applies category weights correctly', () => {
        const result = calculateSiteSeoScore({
            content: 100,
            technical: 0,
            linkRedirect: 0,
            structuredSocial: 0,
            performance: 0,
        });
        // content is 30% of the score
        expect(result.score).toBe(30);
        const contentItem = result.breakdown.find((b) => b.category === 'content');
        expect(contentItem?.contribution).toBe(30);
    });
    it('defaults performance to neutral 70 when omitted', () => {
        const result = calculateSiteSeoScore({
            content: 0,
            technical: 0,
            linkRedirect: 0,
            structuredSocial: 0,
        });
        // performance is 10% weight * 70 = 7
        expect(result.score).toBe(7);
    });
    it('clamps out-of-range sub-scores', () => {
        const result = calculateSiteSeoScore({
            content: 200,
            technical: -50,
            linkRedirect: 50,
            structuredSocial: 50,
            performance: 50,
        });
        expect(result.score).toBeGreaterThanOrEqual(0);
        expect(result.score).toBeLessThanOrEqual(100);
    });
});
//# sourceMappingURL=score.test.js.map