import AxeBuilder from '@axe-core/playwright';
import { expect, test, type Page } from '@playwright/test';

const email = process.env.E2E_EMAIL;
const password = process.env.E2E_PASSWORD;

if (process.env.CI && (!email || !password)) {
    throw new Error('E2E_EMAIL and E2E_PASSWORD are required for the Phase 6 activity route in CI.');
}

const login = async (page: Page): Promise<void> => {
    await page.goto('/login');
    await page.getByRole('textbox', { name: 'Username', exact: true }).fill(email!);
    await page.getByLabel('Password').fill(password!);
    await page.getByRole('button', { name: 'Login' }).click();
    await expect(page).toHaveURL(/\/$/);
};

const expectPersistentShell = async (page: Page): Promise<void> => {
    await expect(page.locator('.application-shell')).toHaveCount(1);
    await expect(page.locator('#application-navigation')).toHaveCount(1);
    await expect(page.locator('#application-content')).toHaveCount(1);
    await expect(page.locator('#top-menu, #left-menu')).toHaveCount(0);
};

test('Phase 6 activity keeps Laravel data rules inside the persistent Vue shell', async ({ page }, testInfo) => {
    test.skip(!email || !password, 'Set E2E_EMAIL and E2E_PASSWORD to validate the Phase 6 activity route.');

    const consoleProblems: string[] = [];

    page.on('console', (message) => {
        if (message.type() === 'error' || message.type() === 'warning') {
            consoleProblems.push(`${message.type()}: ${message.text()}`);
        }
    });
    page.on('pageerror', (error) => consoleProblems.push(`pageerror: ${error.message}`));

    await login(page);

    if (testInfo.project.name === 'desktop-chromium') {
        const activityLink = page.locator('a[href$="/activity"]').first();

        await expect(activityLink).toBeVisible();
        await activityLink.click();
    } else {
        await page.goto('/activity');
    }

    await expect(page).toHaveURL(/\/activity$/);
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expectPersistentShell(page);
    await expect(page.getByRole('heading', { level: 1, name: 'Activité' })).toBeVisible();
    await expect(page.getByRole('heading', { level: 2, name: 'Historique récent' })).toBeVisible();

    const geometry = await page.evaluate(() => {
        const content = document.querySelector('#application-content')?.getBoundingClientRect();
        const feed = document.querySelector('.ui-latest-updates')?.getBoundingClientRect();

        return content && feed ? {
            leftInset: feed.left - content.left,
            rightInset: content.right - feed.right,
            feedWidth: feed.width,
        } : null;
    });

    expect(geometry).not.toBeNull();
    expect(geometry?.leftInset ?? 0).toBeGreaterThanOrEqual(15);
    expect(geometry?.rightInset ?? 0).toBeGreaterThanOrEqual(15);
    expect(geometry?.feedWidth ?? Number.POSITIVE_INFINITY).toBeLessThanOrEqual(960);
    if (testInfo.project.name === 'desktop-chromium') {
        expect(geometry?.feedWidth ?? 0).toBeGreaterThanOrEqual(959);
    }
    expect(await page.evaluate(() => document.documentElement.scrollWidth <= document.documentElement.clientWidth + 1)).toBe(true);

    const shellMarker = await page.locator('.application-shell').evaluate((shell) => {
        const marker = crypto.randomUUID();

        (shell as HTMLElement & { __phaseSixMarker?: string }).__phaseSixMarker = marker;

        return marker;
    });
    const activityFilters = page.getByRole('navigation', { name: 'Filtres d’activité' });
    const addedFilter = activityFilters
        .getByRole('link', { name: /Ajouté/ });

    await addedFilter.focus();
    expect(await addedFilter.evaluate((element) => getComputedStyle(element).boxShadow)).not.toBe('none');
    await addedFilter.press('Enter');
    await expect(page).toHaveURL(/\/activity\?activity=added$/);
    await expect(addedFilter).toHaveAttribute('aria-current', 'true');
    await expect(addedFilter).toBeFocused();
    await expect.poll(() => page.locator('.application-shell').evaluate((shell) => (
        shell as HTMLElement & { __phaseSixMarker?: string }
    ).__phaseSixMarker)).toBe(shellMarker);

    await page.goBack();
    await expect(page).toHaveURL(/\/activity$/);
    await expect(activityFilters.getByRole('link', { name: /Tout/ })).toHaveAttribute('aria-current', 'true');

    const firstActivityRow = page.locator('.ui-update-row--clickable').first();

    await firstActivityRow.focus();
    expect(await firstActivityRow.evaluate((element) => getComputedStyle(element).boxShadow)).not.toBe('none');

    const pageTwo = page.getByRole('navigation', { name: 'Pagination de l’activité' })
        .getByRole('link', { name: '2', exact: true });

    await expect(pageTwo).toBeVisible();
    await pageTwo.focus();
    await pageTwo.press('Enter');
    await expect(page).toHaveURL(/\/activity\?page=2$/);
    const currentPage = page.getByRole('navigation', { name: 'Pagination de l’activité' })
        .getByRole('link', { name: '2', exact: true });

    await expect(currentPage).toHaveAttribute('aria-current', 'page');
    await expect(currentPage).toBeFocused();
    await expect.poll(() => page.locator('.application-shell').evaluate((shell) => (
        shell as HTMLElement & { __phaseSixMarker?: string }
    ).__phaseSixMarker)).toBe(shellMarker);

    await page.goto('/activity?activity=not-a-filter');
    await expect(page.getByRole('navigation', { name: 'Filtres d’activité' })
        .getByRole('link', { name: /Tout/ })).toHaveAttribute('aria-current', 'true');
    await expect(page.locator('.application-shell__navigation-skeleton')).toHaveCount(0);

    const axe = await new AxeBuilder({ page })
        .withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'])
        .analyze();
    const blockingViolations = axe.violations.filter((violation) => (
        violation.impact === 'critical' || violation.impact === 'serious'
    ));

    expect(blockingViolations).toEqual([]);
    await page.screenshot({
        path: testInfo.outputPath(`activity-phase6-${testInfo.project.name}.png`),
        fullPage: true,
    });
    expect(consoleProblems).toEqual([]);
});

test('Phase 6 activity keeps record details and the Phase 8 Home route inside the persistent Vue shell', async ({ page }, testInfo) => {
    test.skip(testInfo.project.name !== 'desktop-chromium', 'The mixed Vue and native document boundaries need one browser project.');
    test.skip(!email || !password, 'Set E2E_EMAIL and E2E_PASSWORD to validate the Phase 6 activity route.');

    await login(page);
    await page.goto('/activity');

    const row = page.locator('.ui-update-row--clickable').first();

    await expect(row).toBeVisible();
    await page.evaluate(() => {
        (window as typeof window & { __phaseSixDocument?: boolean }).__phaseSixDocument = true;
    });
    await row.click();
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expect(page.locator('.application-shell')).toHaveCount(1);
    await expect(page.locator('#top-menu, #left-menu')).toHaveCount(0);
    expect(await page.evaluate(() => (
        window as typeof window & { __phaseSixDocument?: boolean }
    ).__phaseSixDocument)).toBe(true);

    await page.goto('/activity');
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expect(page.getByRole('heading', { level: 1, name: 'Activité' })).toBeVisible();

    const shellMarker = await page.locator('.application-shell').evaluate((shell) => {
        const marker = crypto.randomUUID();

        (shell as HTMLElement & { __phaseEightMarker?: string }).__phaseEightMarker = marker;

        return marker;
    });

    await page.locator('#application-content').getByRole('link', { name: 'Home', exact: true }).click();
    await expect(page).toHaveURL(/\/$/);
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expect(page.getByRole('heading', { level: 1, name: 'Home' })).toBeVisible();
    await expect(page.locator('#top-menu, #left-menu')).toHaveCount(0);
    await expect.poll(() => page.locator('.application-shell').evaluate((shell) => (
        shell as HTMLElement & { __phaseEightMarker?: string }
    ).__phaseEightMarker)).toBe(shellMarker);
});
