import { test, expect, type Page } from './fixtures';

// Regression coverage for the reader page-sizing fix: pages must render at a
// consistent width regardless of their intrinsic dimensions, and a long
// (tall) page must extend downward and scroll instead of collapsing into a
// thin vertical stripe.
//
// The failure this guards against: sizing used to be height-driven
// (`max-height: 90vh` in single mode; unbounded natural width in continuous
// mode), so a tall webtoon strip shrank to a sliver and a narrow scan and a
// wide scan rendered at different widths.

const MANGA_ID = 'm1';
const CHAPTER_ID = 'c1';

// A capped reading column of ~700px on desktop; both modes should honour it.
const READING_WIDTH = 700;

const manga = {
    id: MANGA_ID,
    title: 'Sizing Test',
    status: 'ongoing',
    alt_titles: [],
    description: null,
    cover_image_path: null,
    created_at: '2026-01-01T00:00:00Z',
    updated_at: '2026-01-01T00:00:00Z',
    authors: [],
    genres: [],
    tags: [],
    content_warnings: [],
    chapter_storage_bytes: 0
};

const chapter = {
    id: CHAPTER_ID,
    manga_id: MANGA_ID,
    number: 1,
    title: null,
    page_count: 2,
    created_at: '2026-01-01T00:00:00Z',
    size_bytes: 0
};

// Page 1 is wide (1400×900), page 2 is a tall webtoon strip (800×4000).
const pages = [
    { id: 'p1', chapter_id: CHAPTER_ID, page_number: 1, storage_key: 'p/wide', content_type: 'image/svg+xml' },
    { id: 'p2', chapter_id: CHAPTER_ID, page_number: 2, storage_key: 'p/tall', content_type: 'image/svg+xml' }
];

// An SVG with an explicit width/height gives the <img> a real intrinsic
// size, so the rendered aspect ratio matches a genuine page image.
function svg(w: number, h: number): string {
    return `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}"><rect width="${w}" height="${h}" fill="#888"/></svg>`;
}

async function mockReader(page: Page) {
    // Single dispatcher over the whole API surface — takes precedence over
    // the fixture's 503 fallback and avoids route-glob precedence puzzles.
    await page.route('**/api/v1/**', async (route) => {
        const { pathname } = new URL(route.request().url());

        const json = (status: number, body: unknown) =>
            route.fulfill({ status, contentType: 'application/json', body: JSON.stringify(body) });

        // Images.
        if (pathname.includes('/files/p/wide')) {
            return route.fulfill({ status: 200, contentType: 'image/svg+xml', body: svg(1400, 900) });
        }
        if (pathname.includes('/files/p/tall')) {
            return route.fulfill({ status: 200, contentType: 'image/svg+xml', body: svg(800, 4000) });
        }

        // Auth: public, anonymous.
        if (pathname.endsWith('/auth/config')) {
            return json(200, { self_register_enabled: true, private_mode: false });
        }
        if (pathname.endsWith('/auth/me')) return json(401, { error: { code: 'unauthenticated', message: 'no' } });
        if (pathname.endsWith('/auth/me/preferences')) {
            return json(401, { error: { code: 'unauthenticated', message: 'no' } });
        }

        // Reader data.
        if (pathname.endsWith(`/mangas/${MANGA_ID}/chapters/${CHAPTER_ID}/pages`)) {
            return json(200, { pages });
        }
        if (pathname.endsWith(`/mangas/${MANGA_ID}/chapters/${CHAPTER_ID}`)) return json(200, chapter);
        if (pathname.includes(`/mangas/${MANGA_ID}/chapters`)) {
            return json(200, { items: [chapter], page: { limit: 200, offset: 0, total: 1 } });
        }
        if (pathname.endsWith(`/mangas/${MANGA_ID}`)) return json(200, manga);
        if (pathname.includes(`/me/read-progress/${MANGA_ID}`)) {
            return json(401, { error: { code: 'unauthenticated', message: 'no' } });
        }

        console.warn(`[e2e] reader spec unmocked: ${pathname}`);
        return json(503, { error: { code: 'e2e_unmocked', message: pathname } });
    });
}

test('continuous mode renders all pages at a consistent capped width; tall pages scroll', async ({ page }) => {
    await page.addInitScript(() => localStorage.setItem('mangalord-reader-mode', 'continuous'));
    await mockReader(page);

    await page.goto(`/manga/${MANGA_ID}/chapter/${CHAPTER_ID}`);

    const wide = page.getByTestId('reader-page-1');
    const tall = page.getByTestId('reader-page-2');
    await expect(wide).toBeVisible();
    await expect(tall).toBeVisible();

    const wideBox = await wide.boundingBox();
    const tallBox = await tall.boundingBox();
    const viewport = page.viewportSize();
    if (!wideBox || !tallBox || !viewport) throw new Error('missing layout metrics');

    // 1. Consistent width regardless of intrinsic dimensions.
    expect(Math.abs(wideBox.width - tallBox.width)).toBeLessThanOrEqual(1);

    // 2. Capped reading column on desktop (not full-bleed, not a stripe).
    expect(wideBox.width).toBeGreaterThan(READING_WIDTH - 40);
    expect(wideBox.width).toBeLessThanOrEqual(READING_WIDTH + 2);

    // 3. The tall page extends past the viewport (scrolls, doesn't shrink).
    expect(tallBox.height).toBeGreaterThan(viewport.height);
});

test('single mode renders a tall page at the capped width, not a thin stripe', async ({ page }) => {
    await mockReader(page); // default mode is single

    await page.goto(`/manga/${MANGA_ID}/chapter/${CHAPTER_ID}`);

    const img = page.getByTestId('reader-page');
    await expect(img).toBeVisible();

    // Advance from the wide first page to the tall second page.
    await page.getByTestId('reader-next').click();
    await expect(img).toHaveAttribute('src', /tall/);

    const box = await img.boundingBox();
    const viewport = page.viewportSize();
    if (!box || !viewport) throw new Error('missing layout metrics');

    // Width is the capped reading column, not a sliver.
    expect(box.width).toBeGreaterThan(READING_WIDTH - 40);
    expect(box.width).toBeLessThanOrEqual(READING_WIDTH + 2);
    // And the page runs past the viewport so the reader scrolls it.
    expect(box.height).toBeGreaterThan(viewport.height);
});
