import { test, expect, Page } from '@playwright/test';
import { BASE_URL, OPERATIONS, USER, loginAs, select2Fill } from '../helpers';

test.describe('WFBO Karera Bet Offerings', () => {

    async function ensureArenaExists(page: Page): Promise<string> {
        const arenaName = `Bet Test Arena ${Date.now()}`;
        await page.goto(`${BASE_URL}/wfbo/karera/arenas/new`);
        await page.fill('[data-testid="input-name"]', arenaName);
        await page.click('[data-testid="btn-submit"]');
        await expect(page).toHaveURL(`${BASE_URL}/wfbo/karera/arenas`);
        return arenaName;
    }

    async function createEvent(page: Page, eventDate: string, numberOfRaces: number): Promise<string> {
        const arenaName = await ensureArenaExists(page);

        await page.goto(`${BASE_URL}/wfbo/karera/events/new`);
        await page.fill('[data-testid="input-event-date"]', eventDate);
        await page.selectOption('[data-testid="select-arena"]', { label: arenaName });
        await page.fill('[data-testid="input-number-of-races"]', String(numberOfRaces));
        await page.click('[data-testid="btn-submit"]');

        await expect(page.locator('[data-testid="races-grid"]')).toBeVisible();
        return page.url();
    }

    /** Race cards render in raceNumber-ascending order, so index 0 == Race 1, etc. */
    async function getOrderedRaceIds(page: Page): Promise<string[]> {
        const testIds = await page.locator('[data-testid^="race-card-"]').evaluateAll(
            (els) => els.map((el) => el.getAttribute('data-testid') ?? '')
        );
        return testIds.map((testId) => testId.replace('race-card-', ''));
    }

    /** Playwright's `form` option can't express repeated `race_ids[]` params, so build the body manually. */
    async function postBetOffering(page: Page, eventId: string, betType: string, raceIds: string[]) {
        const token = await page.evaluate(() => {
            const form = document.querySelector('[data-testid="multi-race-bet-form"]');
            const input = form?.querySelector('input[name="_token"]') as HTMLInputElement | null;
            return input?.value ?? '';
        });

        const body = new URLSearchParams();
        body.append('_token', token);
        body.append('bet_type', betType);
        for (const raceId of raceIds) {
            body.append('race_ids[]', raceId);
        }

        return page.request.post(`${BASE_URL}/wfbo/karera/events/${eventId}/bet-offerings`, {
            headers: { 'content-type': 'application/x-www-form-urlencoded' },
            data: body.toString(),
            maxRedirects: 0,
        });
    }

    async function addEntriesToFirstRace(page: Page, eventUrl: string, count: number): Promise<void> {
        const raceCard = page.locator('[data-testid^="race-card-"]').first();
        await raceCard.locator('[data-testid^="btn-actions-race-"]').click();
        await raceCard.locator('[data-testid^="btn-view-race-"]').click();

        for (let i = 0; i < count; i++) {
            await page.click('[data-testid="btn-add-entry"]');
            await expect(page.locator('[data-testid="entry-form"]')).toBeVisible();
            await page.fill('[data-testid="input-number"]', String(i + 1));
            await select2Fill(page, 'input-horse-name', `Horse ${Date.now()}-${i}`);
            await select2Fill(page, 'input-jockey-name', `Jockey ${Date.now()}-${i}`);
            await select2Fill(page, 'input-trainer-name', `Trainer ${Date.now()}-${i}`);
            await page.click('[data-testid="btn-submit"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
        }

        await page.goto(eventUrl);
    }

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

        test('regular user cannot submit a bet-offering form', async ({ page }) => {
            await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
            const eventUrl = await createEvent(page, '2026-09-01', 1);
            const eventId = eventUrl.split('/').pop();
            const [raceId] = await getOrderedRaceIds(page);

            await loginAs(page, USER.mobile, USER.password);
            const response = await postBetOffering(page, eventId!, 'win', [raceId]);

            expect(response.status()).toBeGreaterThanOrEqual(400);
        });

    });

    test.describe('single-race bet types as operations', () => {

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

        test('Win is disabled when starters are below the minimum', async ({ page }) => {
            await createEvent(page, '2026-09-02', 1);
            const [raceId] = await getOrderedRaceIds(page);

            const enableBtn = page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`);
            await expect(enableBtn).toBeDisabled();
        });

        test('operations user can enable Win once minimum starters are met', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-03', 1);
            const [raceId] = await getOrderedRaceIds(page);
            await addEntriesToFirstRace(page, eventUrl, 1);

            const enableBtn = page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`);
            await expect(enableBtn).toBeEnabled();
            await enableBtn.click();

            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid^="bet-type-status-"]').first()).toHaveValue('open');
        });

        test('toggling a single-race bet type between open and closed persists', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-04', 1);
            const [raceId] = await getOrderedRaceIds(page);
            await addEntriesToFirstRace(page, eventUrl, 1);

            await page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`).click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            page.once('dialog', dialog => dialog.accept());
            const statusSelect = page.locator('[data-testid^="bet-type-status-"]').first();
            await statusSelect.selectOption('closed');
            await expect(page.locator('[data-testid^="bet-type-status-"]').first()).toHaveValue('closed');
        });

        test('removing an enabled single-race bet type deletes the offering', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-05', 1);
            const [raceId] = await getOrderedRaceIds(page);
            await addEntriesToFirstRace(page, eventUrl, 1);

            await page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`).click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            page.once('dialog', dialog => dialog.accept());
            const removeBtn = page.locator('[data-testid^="btn-remove-bet-type-"]').first();
            await removeBtn.click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`)).toBeVisible();
        });

        test('enabling the same bet type twice for an event is rejected', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-06', 1);
            const eventId = eventUrl.split('/').pop();
            const [raceId] = await getOrderedRaceIds(page);
            await addEntriesToFirstRace(page, eventUrl, 1);

            await page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`).click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            await postBetOffering(page, eventId!, 'win', [raceId]);

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

    });

    test.describe('multi-race bet types as operations', () => {

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

        test('multi-race bets list shows the empty state when none are configured', async ({ page }) => {
            await createEvent(page, '2026-09-07', 2);

            await expect(page.locator('[data-testid="empty-state-multi-bets"]')).toBeVisible();
        });

        test('admin can add a Daily Double across two consecutive races', async ({ page }) => {
            await createEvent(page, '2026-09-08', 2);

            await page.click('[data-testid="btn-add-multi-race-bet"]');
            await expect(page.locator('[data-testid="multi-race-bet-form"]')).toBeVisible();
            await page.selectOption('[data-testid="select-multi-bet-type"]', { value: 'daily_double' });

            const raceIds = await getOrderedRaceIds(page);
            for (const raceId of raceIds) {
                await page.check(`[data-testid="checkbox-race-${raceId}"]`);
            }
            await page.click('[data-testid="btn-submit-multi-race-bet"]');

            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid="multi-race-bets-list"]')).toBeVisible();
            await expect(page.locator('[data-testid^="multi-race-bet-row-"]')).toHaveCount(1);
        });

        test('adding a Daily Double across non-consecutive races is rejected', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-09', 3);
            const eventId = eventUrl.split('/').pop();
            const [race1, , race3] = await getOrderedRaceIds(page);

            const response = await postBetOffering(page, eventId!, 'daily_double', [race1, race3]);
            expect(response.status()).toBe(302);

            await page.reload();
            await expect(page.locator('[data-testid="flash-error"]')).toBeVisible();
            await expect(page.locator('[data-testid="empty-state-multi-bets"]')).toBeVisible();
        });

        test('admin can add an Extra Double across two non-consecutive races', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-10', 3);
            const eventId = eventUrl.split('/').pop();
            const [race1, , race3] = await getOrderedRaceIds(page);

            const response = await postBetOffering(page, eventId!, 'extra_double', [race1, race3]);
            expect(response.status()).toBe(302);

            await page.reload();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid^="multi-race-bet-row-"]')).toHaveCount(1);
        });

        test('adding Pick 4 with only three selected races is rejected', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-11', 3);
            const eventId = eventUrl.split('/').pop();
            const raceIds = await getOrderedRaceIds(page);

            const response = await postBetOffering(page, eventId!, 'pick_4', raceIds);
            expect(response.status()).toBe(302);

            await page.reload();
            await expect(page.locator('[data-testid="flash-error"]')).toBeVisible();
            await expect(page.locator('[data-testid="empty-state-multi-bets"]')).toBeVisible();
        });

        test('deleting a multi-race bet offering removes it from the list', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-12', 2);
            const eventId = eventUrl.split('/').pop();
            const raceIds = await getOrderedRaceIds(page);

            await postBetOffering(page, eventId!, 'daily_double', raceIds);
            await page.reload();
            await expect(page.locator('[data-testid^="multi-race-bet-row-"]')).toHaveCount(1);

            await page.locator('[data-testid^="btn-actions-multi-bet-"]').first().click();
            const deleteBtn = page.locator('[data-testid^="btn-delete-multi-bet-"]').first();
            page.once('dialog', dialog => dialog.accept());
            await deleteBtn.click();

            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid="empty-state-multi-bets"]')).toBeVisible();
        });

        test('deleting a race used by a bet offering is blocked', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-13', 2);
            const eventId = eventUrl.split('/').pop();
            const raceIds = await getOrderedRaceIds(page);

            await postBetOffering(page, eventId!, 'daily_double', raceIds);
            await page.reload();

            await page.locator('[data-testid^="btn-actions-race-"]').first().click();
            const deleteRaceBtn = page.locator('[data-testid^="btn-delete-race-"]').first();
            page.once('dialog', dialog => dialog.accept());
            await deleteRaceBtn.click();

            await expect(page.locator('[data-testid="flash-error"]')).toBeVisible();
            await expect(page.locator('[data-testid^="race-card-"]')).toHaveCount(2);
        });

    });

    test.describe('closing a race cascades to its bet offerings', () => {

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

        test('closing a race betting status closes single-race and multi-race offerings tied to it', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-14', 2);
            const eventId = eventUrl.split('/').pop();
            const [race1Id, race2Id] = await getOrderedRaceIds(page);

            await addEntriesToFirstRace(page, eventUrl, 1);
            await page.locator(`[data-testid="btn-enable-bet-type-${race1Id}-win"]`).click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            await postBetOffering(page, eventId!, 'daily_double', [race1Id, race2Id]);
            await page.reload();

            const winStatusSelect = page.locator('[data-testid^="bet-type-status-"]').first();
            const dailyDoubleStatusSelect = page.locator('[data-testid^="multi-bet-status-"]').first();
            await expect(winStatusSelect).toHaveValue('open');
            await expect(dailyDoubleStatusSelect).toHaveValue('open');

            page.once('dialog', dialog => dialog.accept());
            const raceStatusSelect = page.locator('[data-testid^="betting-status-"]').first();
            await raceStatusSelect.selectOption('closed');

            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid^="bet-type-status-"]').first()).toHaveText('Closed');
            await expect(page.locator('[data-testid^="multi-bet-status-"]').first()).toHaveText('Closed');
        });

        test('reopening a bet offering is blocked while its race betting is closed', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-15', 1);
            const [raceId] = await getOrderedRaceIds(page);

            await addEntriesToFirstRace(page, eventUrl, 1);
            await page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`).click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            page.once('dialog', dialog => dialog.accept());
            await page.locator('[data-testid^="betting-status-"]').first().selectOption('closed');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid^="bet-type-status-"]').first()).toHaveText('Closed');
        });

    });

    test.describe('entry scratch affects bet eligibility', () => {

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

        test('scratching the only active entry auto-closes the Win offering', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-16', 1);
            const [raceId] = await getOrderedRaceIds(page);
            await addEntriesToFirstRace(page, eventUrl, 1);

            await page.locator(`[data-testid="btn-enable-bet-type-${raceId}-win"]`).click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            await page.locator('[data-testid^="btn-actions-race-"]').first().click();
            await page.locator('[data-testid^="btn-view-race-"]').first().click();

            page.once('dialog', dialog => dialog.accept());
            const scratchBtn = page.locator('[data-testid^="btn-scratch-"]').first();
            await scratchBtn.click();

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

            await page.goto(eventUrl);
            await expect(page.locator('[data-testid^="bet-type-status-"]').first()).toHaveValue('closed');
        });

        test('restoring a scratched entry recovers the entry count', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-17', 1);
            await addEntriesToFirstRace(page, eventUrl, 1);

            await page.locator('[data-testid^="btn-actions-race-"]').first().click();
            await page.locator('[data-testid^="btn-view-race-"]').first().click();

            page.once('dialog', dialog => dialog.accept());
            await page.locator('[data-testid^="btn-scratch-"]').first().click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid^="entry-status-"]').first()).toHaveText('Scratched');

            page.once('dialog', dialog => dialog.accept());
            await page.locator('[data-testid^="btn-restore-"]').first().click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator('[data-testid^="entry-status-"]').first()).toHaveText('Active');
        });

        test('entry count on event page shows scratched count', async ({ page }) => {
            const eventUrl = await createEvent(page, '2026-09-18', 1);
            const [raceId] = await getOrderedRaceIds(page);
            await addEntriesToFirstRace(page, eventUrl, 2);

            const entryCount = page.locator(`[data-testid="entry-count-${raceId}"]`);
            await expect(entryCount).toContainText('2 entries');

            await page.locator('[data-testid^="btn-actions-race-"]').first().click();
            await page.locator('[data-testid^="btn-view-race-"]').first().click();

            page.once('dialog', dialog => dialog.accept());
            await page.locator('[data-testid^="btn-scratch-"]').first().click();
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            await page.goto(eventUrl);
            await expect(entryCount).toContainText('1 entry');
            await expect(entryCount).toContainText('1 scratched');
        });

    });

});
