import { test, expect } from '@playwright/test';
import { BASE_URL, ADMIN, AGENT, USER, loginAs, uniqueFutureDate } from '../helpers';

test.describe('WFBO Event Management', () => {

    test.describe('access control', () => {

        test('admin can access events page', async ({ page }) => {
            await loginAs(page, ADMIN.mobile, ADMIN.password);
            await page.goto(`${BASE_URL}/wfbo/events`);

            await expect(page.locator('[data-testid="wfbo-sidebar"]')).toBeVisible();
        });

        test('regular user cannot access WFBO events', async ({ page }) => {
            await loginAs(page, USER.mobile, USER.password);
            await page.goto(`${BASE_URL}/wfbo/events`);

            await expect(page.locator('[data-testid="wfbo-sidebar"]')).not.toBeVisible();
        });

        test('agent cannot access WFBO events', async ({ page }) => {
            await loginAs(page, AGENT.mobile, AGENT.password);
            await page.goto(`${BASE_URL}/wfbo/events`);

            await expect(page.locator('[data-testid="wfbo-sidebar"]')).not.toBeVisible();
        });

        test('unauthenticated user is redirected away from events', async ({ page }) => {
            await page.context().clearCookies();
            await page.goto(`${BASE_URL}/wfbo/events`);

            expect(page.url()).not.toContain('/wfbo/events');
        });

    });

    test.describe('CRUD as admin', () => {

        test.beforeEach(async ({ page }) => {
            await loginAs(page, ADMIN.mobile, ADMIN.password);
        });

        test('events page loads with Add New button', async ({ page }) => {
            await page.goto(`${BASE_URL}/wfbo/events`);

            await expect(page.locator('[data-testid="wfbo-sidebar"]')).toBeVisible();
            await expect(page.locator('[data-testid="btn-new-event"]')).toBeVisible();
        });

        test('new event form loads with arena, date and video link fields but no status field', async ({ page }) => {
            await page.goto(`${BASE_URL}/wfbo/events/new`);

            const form = page.locator('[data-testid="event-form"]');
            await expect(form).toBeVisible();
            await expect(page.locator('[data-testid="select-event-management"]')).toBeVisible();
            await expect(page.locator('[data-testid="input-event-date"]')).toBeVisible();
            await expect(page.locator('[data-testid="input-event-video-url"]')).toBeVisible();
            await expect(page.locator('[data-testid="btn-submit"]')).toBeVisible();

            // Status must never be exposed in the create/edit form.
            await expect(form.locator('[data-testid*="status" i]')).toHaveCount(0);
            await expect(form.locator('[name*="status" i]')).toHaveCount(0);
            await expect(form).not.toContainText('Status');
            await expect(form).not.toContainText('Published');
        });

        test('can create a new event, which always starts as Draft', async ({ page }) => {
            const dateStr = uniqueFutureDate();
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
            await page.fill('[data-testid="input-event-date"]', dateStr);
            await page.fill('[data-testid="input-event-video-url"]', 'https://example.com/stream');
            await page.click('[data-testid="btn-submit"]');

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid="events-table"]')).toBeVisible();

            // Event lists are sorted by event date, not creation order, so find our
            // row by its unique date rather than assuming list position.
            const row = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            await expect(row).toContainText('Draft');
            await expect(row).not.toContainText('Published');
        });

        test('arena name still shows after reloading the list (cached read)', async ({ page }) => {
            const dateStr = uniqueFutureDate();
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            const arenaName = await page.locator('[data-testid="select-event-management"] option').nth(1).textContent();
            await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
            await page.fill('[data-testid="input-event-date"]', dateStr);
            await page.click('[data-testid="btn-submit"]');

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);

            const row = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            await expect(row.locator('td').first()).toHaveText(arenaName ?? '');

            // The first render after save always hits a fresh DB query (cache was
            // just invalidated) - the regression only shows up once the list is
            // served from the cached read, i.e. on a subsequent load.
            await page.reload();
            const rowAfterReload = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            await expect(rowAfterReload.locator('td').first()).toHaveText(arenaName ?? '');
        });

        test('submitting without selecting an arena shows errors', async ({ page }) => {
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            await page.fill('[data-testid="input-event-date"]', uniqueFutureDate());
            await page.click('[data-testid="btn-submit"]');

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events/new`);
        });

        test('can edit an existing event', async ({ page }) => {
            const dateStr = uniqueFutureDate();
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
            await page.fill('[data-testid="input-event-date"]', dateStr);
            await page.click('[data-testid="btn-submit"]');

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);

            const row     = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            const editBtn = row.locator('[data-testid^="btn-edit-"]');

            await editBtn.click();
            await expect(page.locator('[data-testid="event-form"]')).toBeVisible();

            await page.fill('[data-testid="input-event-video-url"]', 'https://example.com/updated');
            await page.click('[data-testid="btn-submit"]');

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
        });

        test('a pasted YouTube share/live link is saved as the embed link', async ({ page }) => {
            const dateStr = uniqueFutureDate();
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
            await page.fill('[data-testid="input-event-date"]', dateStr);
            // Exactly what YouTube's Share button gives for a live stream.
            await page.fill('[data-testid="input-event-video-url"]', 'https://www.youtube.com/live/ooK9U5tlxpo?si=ap44OUDEw9YI2xze');
            await expect(page.locator('[data-testid="event-form"]')).toContainText('Paste any YouTube link');
            await page.click('[data-testid="btn-submit"]');
            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);

            const row = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            await expect(row.locator('a', { hasText: 'Link' })).toHaveAttribute('href', 'https://www.youtube.com/embed/ooK9U5tlxpo');

            await row.locator('[data-testid^="btn-edit-"]').click();
            await expect(page.locator('[data-testid="input-event-video-url"]')).toHaveValue('https://www.youtube.com/embed/ooK9U5tlxpo');

            // A youtu.be short link works too.
            await page.fill('[data-testid="input-event-video-url"]', 'https://youtu.be/ooK9U5tlxpo?si=abc');
            await page.click('[data-testid="btn-submit"]');
            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);
            await expect(row.locator('a', { hasText: 'Link' })).toHaveAttribute('href', 'https://www.youtube.com/embed/ooK9U5tlxpo');
        });

        test('can delete an event', async ({ page }) => {
            const dateStr = uniqueFutureDate();
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
            await page.fill('[data-testid="input-event-date"]', dateStr);
            await page.click('[data-testid="btn-submit"]');

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);

            const row       = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            const deleteBtn = row.locator('[data-testid^="btn-delete-"]');

            page.once('dialog', dialog => dialog.accept());
            await deleteBtn.click();

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
        });

        test('deleting an event that still has a race is blocked with a friendly error instead of crashing', async ({ page }) => {
            const dateStr = uniqueFutureDate();
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
            await page.fill('[data-testid="input-event-date"]', dateStr);
            await page.click('[data-testid="btn-submit"]');
            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);

            const row       = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            const eventHref = await row.locator('[data-testid^="btn-view-"]').getAttribute('href');
            await page.goto(`${BASE_URL}${eventHref}`);
            await page.click('[data-testid="btn-new-race"]');
            await page.fill('[data-testid="input-race-number"]', '1');
            await page.click('[data-testid="btn-submit"]');

            await page.goto(`${BASE_URL}/wfbo/events`);
            const deleteBtn = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr }).locator('[data-testid^="btn-delete-"]');
            page.once('dialog', dialog => dialog.accept());
            await deleteBtn.click();

            await expect(page.locator('[data-testid="flash-error"]')).toBeVisible();
            await expect(page.locator('[data-testid="flash-error"]')).toContainText('races');

            // The event itself was not deleted.
            await page.goto(`${BASE_URL}/wfbo/events`);
            await expect(page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr })).toHaveCount(1);
        });

        test('can publish an event from the view page', async ({ page }) => {
            const dateStr = uniqueFutureDate();
            await page.goto(`${BASE_URL}/wfbo/events/new`);
            await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
            await page.fill('[data-testid="input-event-date"]', dateStr);
            await page.click('[data-testid="btn-submit"]');

            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events`);
            const row       = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateStr });
            const eventHref = await row.locator('[data-testid^="btn-view-"]').getAttribute('href');
            await page.goto(`${BASE_URL}${eventHref}`);

            await expect(page.locator('[data-testid="event-status"]')).toHaveText('Draft');
            await expect(page.locator('[data-testid="btn-publish-event"]')).toBeVisible();

            page.once('dialog', dialog => dialog.accept());
            await page.click('[data-testid="btn-publish-event"]');

            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid="event-status"]')).toHaveText('Published');
            await expect(page.locator('[data-testid="btn-publish-event"]')).toHaveCount(0);
        });

        test('WFBO sidebar Live Racing group has Event Management link', async ({ page }) => {
            await page.goto(`${BASE_URL}/wfbo/events`);

            const link = page.locator('[data-testid="wfbo-nav-events"]');
            await expect(link).toBeVisible();
            await expect(link).toContainText('Event Management');
        });

        // Run last: creates 12 events dated to outrank every other event in the
        // DB (so "latest on top" / page-boundary assertions are deterministic),
        // which would otherwise push every subsequent test's freshly created
        // event onto page 2 and break its "lands on page 1 with a bare
        // /wfbo/events URL" assumption. Cleans up after itself regardless.
        test('paginates the list 10 per page, newest event date first', async ({ page }) => {
            const base = Date.UTC(2400, 0, 1);
            const dates = Array.from({ length: 12 }, (_, i) =>
                new Date(base + i * 86400000).toISOString().slice(0, 10));

            for (const dateStr of dates) {
                await page.goto(`${BASE_URL}/wfbo/events/new`);
                await page.selectOption('[data-testid="select-event-management"]', { index: 1 });
                await page.fill('[data-testid="input-event-date"]', dateStr);
                await page.click('[data-testid="btn-submit"]');
                await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            }

            // Newest-created (dates[11], the latest date) lands on page 1 - the
            // create redirect always follows the new event to its own page,
            // and the latest date is always page 1 regardless of that.
            await page.goto(`${BASE_URL}/wfbo/events`);
            const rows = page.locator('[data-testid^="event-row-"]');
            await expect(rows).toHaveCount(10);
            await expect(rows.first()).toContainText(dates[11]);
            await expect(page.locator('[data-testid="events-pagination"]')).toBeVisible();
            await expect(page.locator('[data-testid="btn-next-page"]')).toBeVisible();
            await expect(page.locator('[data-testid="btn-prev-page"]')).toHaveCount(0);

            // The two oldest of our 12 (dates[0], dates[1]) are pushed to page 2.
            await page.click('[data-testid="btn-next-page"]');
            await expect(page).toHaveURL(`${BASE_URL}/wfbo/events?page=2`);
            const page2Rows = page.locator('[data-testid^="event-row-"]');
            await expect(page2Rows.filter({ hasText: dates[0] })).toBeVisible();
            await expect(page2Rows.filter({ hasText: dates[11] })).toHaveCount(0);
            await expect(page.locator('[data-testid="btn-prev-page"]')).toBeVisible();

            // Clean up: deleting from page 1 repeatedly always surfaces the next
            // of our own dates to the top until all 12 are gone.
            const dateGroup = new RegExp(dates.join('|'));
            for (let i = 0; i < dates.length; i++) {
                await page.goto(`${BASE_URL}/wfbo/events`);
                const row = page.locator('[data-testid^="event-row-"]').filter({ hasText: dateGroup }).first();
                page.once('dialog', dialog => dialog.accept());
                await row.locator('[data-testid^="btn-delete-"]').click();
                await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            }
        });

    });

});
