import { expect, test } from "@playwright/test";

const site = "http://community-works-collaborative.test/";

// Viewports to audit (mobile / tablet / desktop / wide)
const viewports = [
    { name: "xs-360", width: 360, height: 800 },
    { name: "sm-402", width: 402, height: 874 },
    { name: "md-768", width: 768, height: 1024 },
    { name: "lg-1024", width: 1024, height: 768 },
    { name: "xl-1280", width: 1280, height: 800 },
    { name: "2xl-1440", width: 1440, height: 900 },
    { name: "3xl-1920", width: 1920, height: 1080 },
];

// Common selectors to audit for overflow / alignment
const selectors = {
    header: ".site-header",
    logo: ".site-header__logo",
    navToggle: ".nav-main__toggle",
    main: "main, .site-main, .page-content",
    body: "body",
    hero: ".homepage-hero, .page-hero",
    cards: ".card, .post-card",
    sections: "section",
    footer: ".site-footer, footer",
    grid: ".grid, [class*='grid']",
};

// Check for horizontal overflow at every viewport
for (const vp of viewports) {
    test(`no horizontal overflow at ${vp.name} (${vp.width}x${vp.height})`, async ({
        browser,
    }) => {
        const context = await browser.newContext({
            viewport: { width: vp.width, height: vp.height },
        });
        const page = await context.newPage();
        await page.goto(site, { waitUntil: "networkidle" });

        const overflow = await page.evaluate(() => {
            const docWidth = document.documentElement.scrollWidth;
            const winWidth = window.innerWidth;
            const offenders = [];

            // Find any element wider than the viewport or that pushes the doc to overflow
            if (docWidth > winWidth + 1) {
                const all = document.querySelectorAll("*");
                for (const el of all) {
                    const rect = el.getBoundingClientRect();
                    if (rect.right > winWidth + 1) {
                        offenders.push({
                            tag: el.tagName.toLowerCase(),
                            class: el.className?.toString?.().slice(0, 80),
                            right: Math.round(rect.right),
                            width: Math.round(rect.width),
                        });
                        if (offenders.length >= 10) break;
                    }
                }
            }

            return { docWidth, winWidth, offenders };
        });

        expect(
            overflow.docWidth,
            `Doc width ${overflow.docWidth} > viewport ${overflow.winWidth}. Offenders: ${JSON.stringify(overflow.offenders)}`,
        ).toBeLessThanOrEqual(overflow.winWidth + 1);

        await context.close();
    });
}

// Header alignment per viewport
for (const vp of viewports) {
    test(`header alignment at ${vp.name} (${vp.width}x${vp.height})`, async ({
        browser,
    }) => {
        const context = await browser.newContext({
            viewport: { width: vp.width, height: vp.height },
        });
        const page = await context.newPage();
        await page.goto(site, { waitUntil: "networkidle" });

        const header = page.locator(selectors.header).first();
        await expect(header).toBeVisible();

        const box = await header.boundingBox();
        expect(box).not.toBeNull();

        // Header must start at the left edge (allow 1px sub-pixel)
        expect(Math.abs(box.x)).toBeLessThanOrEqual(1);
        expect(box.width).toBeGreaterThan(vp.width * 0.8);

        await context.close();
    });
}

// Main content padding/alignment per viewport
for (const vp of viewports) {
    test(`main content padding at ${vp.name} (${vp.width}x${vp.height})`, async ({
        browser,
    }) => {
        const context = await browser.newContext({
            viewport: { width: vp.width, height: vp.height },
        });
        const page = await context.newPage();
        await page.goto(site, { waitUntil: "networkidle" });

        const main = page.locator(selectors.main).first();
        const mainCount = await page.locator(selectors.main).count();

        if (mainCount > 0) {
            const box = await main.boundingBox();
            expect(box).not.toBeNull();

            // Check for symmetric horizontal padding (allow 1px)
            const leftPad = box.x;
            const rightPad = vp.width - (box.x + box.width);
            expect(Math.abs(leftPad - rightPad)).toBeLessThanOrEqual(2);
        }

        await context.close();
    });
}

// Section spacing audit
for (const vp of viewports) {
    test(`section vertical rhythm at ${vp.name} (${vp.width}x${vp.height})`, async ({
        browser,
    }) => {
        const context = await browser.newContext({
            viewport: { width: vp.width, height: vp.height },
        });
        const page = await context.newPage();
        await page.goto(site, { waitUntil: "networkidle" });

        // Gather all top-level sections and report top/bottom paddings
        const sectionInfo = await page.evaluate(() => {
            const sections = Array.from(
                document.querySelectorAll("body > * section, main > section"),
            );
            return sections.slice(0, 15).map((s) => {
                const cs = getComputedStyle(s);
                const rect = s.getBoundingClientRect();
                return {
                    class: s.className?.toString?.().slice(0, 60),
                    top: Math.round(rect.top),
                    height: Math.round(rect.height),
                    paddingTop: cs.paddingTop,
                    paddingBottom: cs.paddingBottom,
                    marginTop: cs.marginTop,
                    marginBottom: cs.marginBottom,
                };
            });
        });

        // No section should have 0 vertical padding (visual breathing room)
        for (const s of sectionInfo) {
            const pt = parseFloat(s.paddingTop) || 0;
            const pb = parseFloat(s.paddingBottom) || 0;
            // At least one of top/bottom padding should be > 8px for breathing room
            // (except narrow edge cases)
            if (s.height > 50) {
                expect(
                    Math.max(pt, pb),
                    `Section "${s.class}" has insufficient vertical padding (pt=${pt}, pb=${pb})`,
                ).toBeGreaterThan(8);
            }
        }

        await context.close();
    });
}

// Card grid alignment at common breakpoints
for (const vp of viewports) {
    test(`card alignment at ${vp.name} (${vp.width}x${vp.height})`, async ({
        browser,
    }) => {
        const context = await browser.newContext({
            viewport: { width: vp.width, height: vp.height },
        });
        const page = await context.newPage();
        await page.goto(site, { waitUntil: "networkidle" });

        const cardCount = await page.locator(selectors.cards).count();
        if (cardCount < 2) {
            test.skip();
            return;
        }

        const boxes = await page
            .locator(selectors.cards)
            .evaluateAll((els) =>
                els
                    .slice(0, 12)
                    .map((e) => {
                        const r = e.getBoundingClientRect();
                        return {
                            x: Math.round(r.x),
                            y: Math.round(r.y),
                            w: Math.round(r.width),
                            h: Math.round(r.height),
                        };
                    }),
            );

        // All visible cards should be within the viewport
        for (const b of boxes) {
            expect(b.x).toBeGreaterThanOrEqual(0);
            expect(b.x + b.w).toBeLessThanOrEqual(vp.width + 1);
        }

        // Group by row (similar y) and verify alignment
        const rows = {};
        for (const b of boxes) {
            const rowKey = Math.round(b.y / 10); // tolerance for sub-row alignment
            if (!rows[rowKey]) rows[rowKey] = [];
            rows[rowKey].push(b);
        }

        for (const row of Object.values(rows)) {
            if (row.length < 2) continue;
            // Same-row items should share a common left or right edge
            // (allow 2px tolerance for grid gap math)
            const xs = row.map((b) => b.x);
            const minX = Math.min(...xs);
            const maxX = Math.max(...xs);
            // All should be left-aligned to the same x OR span the row
            // We just want to ensure they're not wildly misaligned
            expect(
                maxX - minX,
                `Cards in same row misaligned: ${JSON.stringify(row)}`,
            ).toBeLessThanOrEqual(vp.width * 0.95);
        }

        await context.close();
    });
}

// Footer alignment
for (const vp of viewports) {
    test(`footer alignment at ${vp.name} (${vp.width}x${vp.height})`, async ({
        browser,
    }) => {
        const context = await browser.newContext({
            viewport: { width: vp.width, height: vp.height },
        });
        const page = await context.newPage();
        await page.goto(site, { waitUntil: "networkidle" });

        const footer = page.locator(selectors.footer).first();
        const count = await page.locator(selectors.footer).count();
        if (count === 0) {
            test.skip();
            return;
        }

        const box = await footer.boundingBox();
        expect(box).not.toBeNull();
        // Footer should not overflow horizontally
        expect(box.x + box.width).toBeLessThanOrEqual(vp.width + 1);

        await context.close();
    });
}

// Image / media overflow
for (const vp of viewports) {
    test(`images fit viewport at ${vp.name} (${vp.width}x${vp.height})`, async ({
        browser,
    }) => {
        const context = await browser.newContext({
            viewport: { width: vp.width, height: vp.height },
        });
        const page = await context.newPage();
        await page.goto(site, { waitUntil: "networkidle" });

        const overflows = await page.evaluate(() => {
            const imgs = Array.from(document.querySelectorAll("img"));
            const out = [];
            for (const img of imgs) {
                const r = img.getBoundingClientRect();
                if (r.right > window.innerWidth + 1 || r.width > window.innerWidth + 1) {
                    out.push({
                        src: img.src.slice(-60),
                        width: Math.round(r.width),
                        right: Math.round(r.right),
                    });
                }
            }
            return out;
        });

        expect(overflows.length, `Images overflow: ${JSON.stringify(overflows)}`).toBe(0);

        await context.close();
    });
}
