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 business-route characterization.');
}

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);
};

const markPersistentShell = async (page: Page): Promise<string> => page
    .locator('.application-shell')
    .evaluate((shell) => {
        const marker = crypto.randomUUID();

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

        return marker;
    });

const expectShellMarker = async (page: Page, marker: string): Promise<void> => {
    await expect.poll(() => page.locator('.application-shell').evaluate((shell) => (
        shell as HTMLElement & { __phaseSixBusinessMarker?: string }
    ).__phaseSixBusinessMarker)).toBe(marker);
};

const expectNoHorizontalPageOverflow = async (page: Page): Promise<void> => {
    const widths = await page.evaluate(() => ({
        clientWidth: document.documentElement.clientWidth,
        scrollWidth: document.documentElement.scrollWidth,
    }));

    expect(widths.scrollWidth, JSON.stringify(widths)).toBeLessThanOrEqual(widths.clientWidth + 1);
};

const expectNoSeriousA11yViolations = async (page: Page): Promise<void> => {
    const results = await new AxeBuilder({ page }).analyze();
    const violations = results.violations.filter((violation) => (
        violation.impact === 'serious' || violation.impact === 'critical'
    ));

    expect(violations, JSON.stringify(violations, null, 2)).toEqual([]);
};

const firstInvestmentContext = async (page: Page): Promise<{
    investmentHref: string;
    fundHref: string;
}> => {
    await page.goto('/investments');
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expectPersistentShell(page);

    const row = page.locator('#investments-crud-table tbody tr').first();

    await expect(row).toBeAttached();
    const investmentHref = await row
        .locator('a.ui-visually-hidden[href*="/investments/"]')
        .getAttribute('href');
    const fundHref = await row
        .locator('a[href*="/funds/"]')
        .getAttribute('href');

    expect(investmentHref).toMatch(/\/investments\/[0-9a-f-]+\?return_index=1$/);
    expect(fundHref).toMatch(/\/funds\/[0-9a-f-]+$/);

    return {
        investmentHref: investmentHref!,
        fundHref: fundHref!,
    };
};

test('Phase 6 business pages cross from the Vue index and keep record workflows inside the persistent shell', async ({ page }, testInfo) => {
    test.skip(testInfo.project.name !== 'desktop-chromium', 'The complete Phase 6 workflow only needs one desktop browser project.');
    test.skip(!email || !password, 'Set E2E_EMAIL and E2E_PASSWORD to validate the Phase 6 business pages.');

    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);
    const { investmentHref, fundHref } = await firstInvestmentContext(page);
    const investmentEditUrl = new URL(investmentHref);
    investmentEditUrl.pathname = `${investmentEditUrl.pathname}/edit`;
    const investmentRow = page.locator('#investments-crud-table tbody tr').first();
    const indexShellMarker = await markPersistentShell(page);

    // Click the commitment cell background rather than one of the embedded
    // entity links; the row activation contract owns navigation to the
    // investment detail.
    await investmentRow.locator('td').nth(3).click();
    await expect(page).toHaveURL(investmentHref);
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expectPersistentShell(page);
    await expectShellMarker(page, indexShellMarker);
    await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
    await expectNoSeriousA11yViolations(page);

    const relatedShellMarker = await markPersistentShell(page);
    const relatedRecords = [
        {
            tab: /Calls & distributions/,
            table: '#cash-flow-detail-table',
            url: /\/call-distribs\/[0-9a-f-]+\?return_investment_index=1$/,
        },
        {
            tab: /Capital accounts/,
            table: '#capital-account-detail-table',
            url: /\/capital-accounts\/[0-9a-f-]+\?return_investment_index=1$/,
        },
    ];

    for (const related of relatedRecords) {
        await page.getByRole('tab', { name: related.tab }).click();
        const row = page.locator(`${related.table} .erp-detail-table__row`).first();

        await expect(row).toBeVisible();
        await row.focus();
        await row.press('Enter');
        await expect(page).toHaveURL(related.url);
        await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
        await expect(page.locator('.info-card')).not.toHaveCount(0);
        await expectShellMarker(page, relatedShellMarker);
        await page.getByRole('link', { name: 'Back to investment', exact: true }).click();
        await expect(page).toHaveURL(investmentHref);
        await expectShellMarker(page, relatedShellMarker);
    }

    const deleteButton = page.getByRole('button', { name: 'Delete', exact: true });

    await deleteButton.focus();
    await deleteButton.click();
    const deleteDialog = page.getByRole('alertdialog', { name: 'Delete investment?' });

    await expect(deleteDialog).toBeVisible();
    await page.keyboard.press('Escape');
    await expect(deleteDialog).toBeHidden();
    await expect(deleteButton).toBeFocused();

    const showShellMarker = await markPersistentShell(page);

    await page.getByRole('link', { name: 'Edit', exact: true }).click();
    await expect(page).toHaveURL(investmentEditUrl.toString());
    await expect(page.getByRole('heading', { level: 1, name: 'Edit investment' })).toBeVisible();
    await expectShellMarker(page, showShellMarker);

    const investorCombobox = page.getByRole('combobox', { name: /^Investor/ });

    await investorCombobox.focus();
    await expect(page.locator('.ui-combobox__menu')).toBeVisible();
    await page.keyboard.press('Escape');
    await expect(page.locator('.ui-combobox__menu')).toBeHidden();
    await expect(investorCombobox).toBeFocused();

    const commitmentDate = page.getByRole('button', { name: 'Commitment date', exact: true });
    const commitmentCalendar = page.locator('#commitment_date_popover');

    await commitmentDate.click();
    await expect(commitmentCalendar).toBeVisible();
    await page.keyboard.press('Escape');
    await expect(commitmentCalendar).toBeHidden();
    await expect(commitmentDate).toBeFocused();

    await page.goto(fundHref);
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expectPersistentShell(page);

    await expect(page.getByRole('tab', { name: /Investors/ })).toHaveAttribute('aria-selected', 'true');
    const fundShellMarker = await markPersistentShell(page);

    await page.getByRole('link', { name: 'Add investment', exact: true }).click();
    await expect(page).toHaveURL(/\/funds\/[0-9a-f-]+\/investments\/create$/);
    await expect(page.getByRole('heading', { level: 1, name: 'New investment' })).toBeVisible();
    await expectShellMarker(page, fundShellMarker);

    const lockedFund = page.getByRole('combobox', { name: /^Fund/ });
    const requiredInvestor = page.getByRole('combobox', { name: /^Investor/ });

    await expect(lockedFund).toBeDisabled();
    await expect(lockedFund).not.toHaveValue('');
    await expect(page.locator('input[type="hidden"][name="fund_id"]')).not.toHaveValue('');
    await expect(requiredInvestor).toBeEnabled();

    await page.getByRole('button', { name: 'Create investment', exact: true }).click();
    await expect(page).toHaveURL(/\/funds\/[0-9a-f-]+\/investments\/create$/);
    await expect(requiredInvestor).toHaveAttribute('aria-invalid', 'true');
    await expect(requiredInvestor).toBeFocused();
    await expect(lockedFund).toBeDisabled();

    await expectNoHorizontalPageOverflow(page);
    await expectNoSeriousA11yViolations(page);
    await page.screenshot({
        path: testInfo.outputPath('phase6-business-form-validation-desktop.png'),
        fullPage: true,
    });
    expect(consoleProblems).toEqual([]);
});

test('Phase 6 submits a valid Vue form, lands on its detail, and cleans up through the shared confirmation', async ({ page }, testInfo) => {
    test.skip(testInfo.project.name !== 'desktop-chromium', 'The valid mutation flow only needs one desktop browser project.');
    test.skip(!email || !password, 'Set E2E_EMAIL and E2E_PASSWORD to validate the Phase 6 business pages.');

    await login(page);
    await page.goto('/exchange-rates/create');
    await expect(page.getByRole('heading', { level: 1, name: 'New exchange rate' })).toBeVisible();

    const source = 'E2E-' + Date.now();

    await page.locator('input[name="cur1"]').fill('chf');
    await page.locator('input[name="cur2"]').fill('sek');
    await page.locator('input[name="rate"]').fill('11.75');
    await page.locator('input[name="source"]').fill(source);
    await page.getByRole('button', { name: 'Rate date', exact: true }).click();
    await page.getByRole('button', { name: 'Today', exact: true }).click();
    await page.getByRole('button', { name: 'Create exchange rate', exact: true }).click();

    await expect(page).toHaveURL(/\/exchange-rates\/[0-9a-f-]+$/);
    await expect(page.getByRole('heading', { level: 1, name: 'CHF / SEK' })).toBeVisible();
    await expect(page.getByText(source, { exact: true })).toBeVisible();
    await expectPersistentShell(page);
    await expectNoHorizontalPageOverflow(page);
    await expectNoSeriousA11yViolations(page);

    await page.getByRole('button', { name: 'Delete', exact: true }).click();
    const dialog = page.getByRole('alertdialog', { name: 'Delete exchange rate?' });

    await expect(dialog).toBeVisible();
    await dialog.getByRole('button', { name: 'Delete exchange rate', exact: true }).click();
    await expect(page).toHaveURL(/\/exchange-rates$/);
});

test('Phase 6 business detail and nested investment form do not overflow the mobile viewport', async ({ page }, testInfo) => {
    test.skip(testInfo.project.name !== 'mobile-chromium', 'The responsive guard runs in the mobile browser project.');
    test.skip(!email || !password, 'Set E2E_EMAIL and E2E_PASSWORD to validate the Phase 6 business pages.');

    await login(page);
    const { investmentHref, fundHref } = await firstInvestmentContext(page);

    await page.goto(investmentHref);
    await expect(page.locator('html')).toHaveAttribute('data-frontend-root', 'inertia');
    await expectPersistentShell(page);
    await expect(page.getByRole('heading', { level: 1 })).toBeVisible();
    await expectNoHorizontalPageOverflow(page);
    await expectNoSeriousA11yViolations(page);

    await page.goto(fundHref);
    await expect(page.getByRole('tab', { name: /Investors/ })).toHaveAttribute('aria-selected', 'true');
    await page.getByRole('link', { name: 'Add investment', exact: true }).click();
    await expect(page.getByRole('heading', { level: 1, name: 'New investment' })).toBeVisible();
    await expect(page.getByRole('combobox', { name: /^Fund/ })).toBeDisabled();
    await expectNoHorizontalPageOverflow(page);
    await expectNoSeriousA11yViolations(page);

    await page.screenshot({
        path: testInfo.outputPath('phase6-business-form-mobile.png'),
        fullPage: true,
    });
});
