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;

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(/\/$/);
};

test('the Vue shell owns global toasts, Excel and the complete teleported account popover', async ({ page }, testInfo) => {
    test.skip(!email || !password, 'Set E2E_EMAIL and E2E_PASSWORD to validate Vue global surfaces.');
    const isMobile = testInfo.project.name === 'mobile-chromium';

    await login(page);
    await page.goto('/__kview/inertia/application-shell');
    await expect(page.getByRole('heading', { name: 'Layout applicatif persistant' })).toBeVisible();

    let releaseExcelResponse: (() => void) | undefined;
    await page.route('**/api/protocol-token/create', async (route) => {
        await new Promise<void>((resolve) => {
            releaseExcelResponse = resolve;
        });
        await route.fulfill({
            status: 200,
            contentType: 'application/json',
            body: JSON.stringify({
                token: 'vue e2e token/+?&',
                url: 'https://kview-investor.test/vue?a=1&b=2',
            }),
        });
    });
    await page.evaluate(() => {
        const protocolLink = document.querySelector<HTMLAnchorElement>('#application-excel-protocol');

        protocolLink?.addEventListener('click', (event) => {
            event.preventDefault();
            const state = window as typeof window & {
                __vueExcelClicks?: number;
                __vueExcelHref?: string | null;
            };
            state.__vueExcelClicks = (state.__vueExcelClicks ?? 0) + 1;
            state.__vueExcelHref = protocolLink.getAttribute('href');
        }, { capture: true });
    });

    const accountTrigger = page.getByRole('button', { name: /Compte de/ });
    const accountPopover = page.locator('#application-account-menu');

    if (isMobile) {
        await accountTrigger.click();
        await expect(accountPopover).toBeVisible();
    }

    const excelTrigger = isMobile
        ? accountPopover.getByRole('button', { name: /Excel/ })
        : page.getByRole('button', { name: 'Excel Login', exact: true });

    await excelTrigger.focus();
    await excelTrigger.press('Enter');

    if (isMobile) {
        await expect(accountPopover).toBeHidden();
        await expect(accountTrigger).toBeFocused();
        await accountTrigger.click();
        await expect(accountPopover).toBeVisible();
    }

    await expect(excelTrigger).toHaveAttribute('aria-busy', 'true');
    await expect(excelTrigger).toBeDisabled();
    await expect(page.locator('.ui-toast--info')).toContainText('Preparing a secure desktop login token');
    expect(await page.evaluate(() => (
        document.querySelector('.ui-toast-viewport')?.contains(document.activeElement) ?? false
    ))).toBe(false);
    await expect(page.locator('.ui-toast-viewport')).toHaveCount(1);
    expect(await page.locator('.ui-toast-viewport').evaluate((element) => element.parentElement === document.body)).toBe(true);

    expect(releaseExcelResponse).toBeDefined();
    releaseExcelResponse?.();

    await expect(excelTrigger).toBeEnabled();
    await expect(page.locator('.ui-toast--success')).toContainText('Excel login ready');
    await expect.poll(() => page.evaluate(() => (
        window as typeof window & { __vueExcelClicks?: number }
    ).__vueExcelClicks)).toBe(1);
    await expect.poll(() => page.evaluate(() => (
        window as typeof window & { __vueExcelHref?: string | null }
    ).__vueExcelHref)).toBe(
        'lncom://token=vue%20e2e%20token%2F%2B%3F%26'
        + '&url=https%3A%2F%2Fkview-investor.test%2Fvue%3Fa%3D1%26b%3D2'
        + '&encoded_url=https%3A%2F%2Fkview-investor.test%2Fvue%3Fa%3D1%26b%3D2',
    );

    if (isMobile) {
        await page.keyboard.press('Escape');
        await expect(accountPopover).toBeHidden();
        await expect(accountTrigger).toBeFocused();
    }

    await page.locator('.ui-toast--success').getByRole('button', { name: /notification/i }).click();
    await expect(page.locator('.ui-toast--success')).toBeHidden();

    await accountTrigger.click();

    await expect(accountPopover).toBeVisible();
    await expect(accountPopover).toHaveAttribute('role', 'dialog');
    await expect(accountTrigger).toHaveAttribute('aria-expanded', 'true');
    await expect(page.getByRole('button', { name: 'Close account menu' })).toBeFocused();
    expect(await accountPopover.evaluate((element) => element.parentElement === document.body)).toBe(true);
    await expect(accountPopover).toContainText('kView INVESTOR by kView');
    await expect(accountPopover.getByRole('link', { name: /User Settings/ })).toHaveAttribute('href', /settings\?tab=user$/);
    await expect(accountPopover.getByRole('link', { name: /Data Status/ })).toHaveAttribute('href', /settings\?tab=system$/);
    await expect(accountPopover.getByRole('link', { name: /Workspace/ })).toHaveAttribute('href', /settings\?tab=workspace$/);
    await expect(accountPopover.getByRole('button', { name: /Excel/ })).toBeVisible();
    await expect(accountPopover.getByRole('button', { name: 'Sign out' })).toBeVisible();
    await expect(accountPopover.locator('.ui-avatar')).toBeVisible();

    const openAccessibility = await new AxeBuilder({ page })
        .withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'])
        .analyze();
    expect(openAccessibility.violations).toEqual([]);

    await page.keyboard.press('Escape');
    await expect(accountPopover).toBeHidden();
    await expect(accountTrigger).toHaveAttribute('aria-expanded', 'false');
    await expect(accountTrigger).toBeFocused();

    await accountTrigger.click();
    const navigationToggle = page.locator('button[aria-controls="application-navigation"]');
    await navigationToggle.click();
    await expect(accountPopover).toBeHidden();

    if (isMobile) {
        await expect(page.getByRole('button', { name: 'Fermer la navigation' })).toBeFocused();
    } else {
        await expect(navigationToggle).toBeFocused();
    }

    await expect(page.locator('[data-ui-toast], [data-user-menu], [data-excel-login-trigger]')).toHaveCount(0);
});

test('the Vue Settings port keeps its dialog and nested floating-layer contracts', async ({ page }) => {
    test.skip(!email || !password, 'Set E2E_EMAIL and E2E_PASSWORD to validate Vue Settings.');

    await login(page);
    await page.goto('/__kview/inertia/application-shell');
    await expect(page.getByRole('heading', { name: 'Layout applicatif persistant' })).toBeVisible();

    const accountTrigger = page.getByRole('button', { name: /Compte de/ });
    await accountTrigger.click();

    const accountPopover = page.locator('#application-account-menu');
    const userSettings = accountPopover.getByRole('link', { name: /User Settings/ });
    await expect(userSettings).toHaveAttribute('href', /settings\?tab=user$/);
    await expect(userSettings).toHaveAttribute('aria-haspopup', 'dialog');
    await expect(userSettings).toHaveAttribute('aria-expanded', 'false');

    const payloadRequestPromise = page.waitForRequest((request) => {
        const url = new URL(request.url());

        return url.pathname === '/settings'
            && url.searchParams.get('tab') === 'user'
            && request.headers().accept === 'application/json';
    });
    await userSettings.click();
    const payloadRequest = await payloadRequestPromise;

    expect(payloadRequest.headers()['x-kview_investor-settings-modal']).toBe('true');

    const dialog = page.getByRole('dialog', { name: 'Settings' });
    const accountTab = dialog.getByRole('tab', { name: 'Account' });

    await expect(dialog).toBeVisible();
    await expect(accountPopover).toBeHidden();
    await expect(accountTab).toHaveAttribute('aria-selected', 'true');
    await expect(accountTab).toBeFocused();
    expect(await dialog.evaluate((element) => (
        element.parentElement?.classList.contains('ui-dialog-layer')
        && element.parentElement.parentElement === document.body
    ))).toBe(true);

    await page.keyboard.press('Control+k');
    await expect(dialog).toBeVisible();
    expect(await dialog.evaluate((element) => element.contains(document.activeElement))).toBe(true);
    await expect(page.locator('.application-shell__search')).toHaveAttribute('aria-expanded', 'false');
    await expect(page.locator('#application-search')).toHaveAttribute('aria-expanded', 'false');

    await dialog.getByRole('tab', { name: 'Preferences' }).click();

    const currency = dialog.getByRole('combobox', { name: 'Dashboard currency' });
    await currency.click();

    const listbox = page.getByRole('listbox');
    const euro = listbox.getByRole('option', { name: 'EUR' });
    await expect(listbox).toBeVisible();
    await expect(euro).toBeVisible();
    await expect(page.locator('.ui-dropdown-menu--reka')).toHaveClass(/ui-dropdown-menu--modal/);

    const triggerBox = await page.locator('#target_currency').boundingBox();
    const menuBox = await page.locator('.ui-dropdown-menu--reka').boundingBox();
    expect(triggerBox).not.toBeNull();
    expect(menuBox).not.toBeNull();
    expect(menuBox!.width).toBeGreaterThanOrEqual(triggerBox!.width - 2);
    expect(await euro.evaluate((element) => {
        const bounds = element.getBoundingClientRect();
        const hit = document.elementFromPoint(
            bounds.left + (bounds.width / 2),
            bounds.top + (bounds.height / 2),
        );

        return Boolean(hit && element.contains(hit));
    })).toBe(true);

    await page.keyboard.press('Escape');
    await expect(listbox).toBeHidden();
    await expect(dialog).toBeVisible();
    await expect(currency).toBeFocused();

    await dialog.getByRole('tab', { name: 'Configuration' }).click();
    const firstEditField = dialog.getByRole('button', { name: /^Edit / }).first();
    await expect(firstEditField).toBeVisible();
    await firstEditField.click();

    const deleteField = dialog.getByRole('button', { name: 'Delete', exact: true });
    await expect(deleteField).toBeVisible();
    await deleteField.focus();
    await deleteField.click();

    const confirmation = page.getByRole('alertdialog', { name: 'Delete dynamic field?' });
    await expect(confirmation).toBeVisible();
    await expect(page.locator('.ui-dialog-layer')).toHaveCount(2);
    expect(await confirmation.evaluate((element) => {
        const layers = Array.from(document.querySelectorAll('.ui-dialog-layer'));

        return element.closest('.ui-dialog-layer') === layers.at(-1);
    })).toBe(true);
    expect(await page.evaluate(() => {
        const overlays = Array.from(document.querySelectorAll<HTMLElement>('.ui-dialog-overlay'));
        const nestedOverlay = overlays.at(-1);
        const hit = document.elementFromPoint(4, 4);

        return Boolean(nestedOverlay && (hit === nestedOverlay || nestedOverlay.contains(hit)));
    })).toBe(true);

    await page.keyboard.press('Escape');
    await expect(confirmation).toBeHidden();
    await expect(dialog).toBeVisible();
    await expect(deleteField).toBeFocused();

    const accessibility = await new AxeBuilder({ page })
        .include('#application-settings-dialog')
        .withTags(['wcag2a', 'wcag2aa', 'wcag21aa', 'wcag22aa'])
        .analyze();
    expect(accessibility.violations).toEqual([]);

    await page.keyboard.press('Escape');
    await expect(dialog).toBeHidden();
    await expect(accountTrigger).toBeFocused();
});
