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

// Event lists are sorted by event date, not creation order, and this DB is shared
// (un-isolated) across spec files in the same test run, so the row we just created
// must be located by its unique date rather than by list position (.last()/.first()).
async function createEventShowUrl(page): Promise<string> {
    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');
    return eventHref ?? '';
}

async function addRaces(page, eventUrl: string, count: number): Promise<string[]> {
    await page.goto(`${BASE_URL}${eventUrl}`);

    for (let i = 1; i <= count; i++) {
        await page.click('[data-testid="btn-new-race"]');
        await page.fill('[data-testid="input-race-number"]', String(i));
        await page.click('[data-testid="btn-submit"]');
    }

    const raceIds = await page.locator('[data-testid^="race-row-"]').evaluateAll(
        rows => rows.map(r => r.getAttribute('data-testid')?.replace('race-row-', ''))
    );
    return raceIds as string[];
}

// Races and Bet Types live under separate tabs on the Event Details page (Races
// is the default tab), so bet-type interactions must land on ?tab=bet-types first.
async function gotoBetTypes(page, eventUrl: string): Promise<void> {
    await page.goto(`${BASE_URL}${eventUrl}?tab=bet-types`);
}

// Multi-race combo rows start empty - clicking "+ Add {type} combo" instantiates
// one from a <template>, at the next zero-based index (0, 1, 2, ...). Returns the
// index of the row just added so callers can build its checkbox/remove testids.
async function addCombo(page, key: string): Promise<number> {
    const button = page.locator(`[data-testid="btn-add-combo-${key}"]`);
    const nextIndex = parseInt(await button.getAttribute('data-add-combo-next') ?? '0', 10);
    await button.click();
    return nextIndex;
}

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

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

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

        test('Races and Bet Types are separate tabs on the event page', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            await addRaces(page, eventUrl, 1);

            // Races tab is the default.
            await expect(page.locator('[data-testid="tab-races"]')).toHaveClass(/is-active/);
            await expect(page.locator('[data-testid="races-table"]')).toBeVisible();
            await expect(page.locator('[data-testid="bet-types-table"]')).toHaveCount(0);

            await page.click('[data-testid="tab-bet-types"]');
            await expect(page).toHaveURL(`${BASE_URL}${eventUrl}?tab=bet-types`);
            await expect(page.locator('[data-testid="tab-bet-types"]')).toHaveClass(/is-active/);
            await expect(page.locator('[data-testid="bet-types-table"]')).toBeVisible();
            await expect(page.locator('[data-testid="races-table"]')).toHaveCount(0);

            await page.click('[data-testid="tab-races"]');
            await expect(page).toHaveURL(`${BASE_URL}${eventUrl}?tab=races`);
            await expect(page.locator('[data-testid="races-table"]')).toBeVisible();
        });

        test('shows a hint instead of the bet types table when the event has no races yet', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            await gotoBetTypes(page, eventUrl);

            await expect(page.locator('[data-testid="bet-types-table"]')).toHaveCount(0);
            await expect(page.getByText('Add races first before configuring bet types.')).toBeVisible();
        });

        test('lists all 14 bet types, split into single-race and multi-race sections', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            await addRaces(page, eventUrl, 1);
            await gotoBetTypes(page, eventUrl);

            await expect(page.locator('[data-testid="bet-types-table"]')).toBeVisible();

            const singleKeys = ['win', 'forecast', 'trifecta', 'quartet', 'pentafecta', 'super_six', 'odd_even'];
            const multiKeys = ['daily_double', 'daily_double_plus_1', 'extra_double', 'pick_4', 'pick_5', 'pick_6', 'wta'];

            for (const key of singleKeys) {
                await expect(page.locator(`[data-testid="bet-type-row-${key}"]`)).toBeVisible();
            }
            for (const key of multiKeys) {
                const card = page.locator(`[data-testid="bet-type-row-${key}"]`);
                await expect(card).toBeVisible();
                // Multi-race cards show a leg-pattern hint instead of a kind badge.
                await expect(card).toContainText('races');
            }

            await expect(page.locator('[data-testid="bet-type-row-extra_double"]')).toContainText('skipped');
            await expect(page.locator('[data-testid="bet-type-row-wta"]')).toContainText('any races');
        });

        test('can offer a single-race bet type on specific races and it persists', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 2);
            await gotoBetTypes(page, eventUrl);

            // Win is single-race: offer it on race 1 only, not race 2.
            await page.check(`[data-testid="checkbox-leg-win-${raceIds[0]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');

            await expect(page).toHaveURL(`${BASE_URL}${eventUrl}?tab=bet-types`);
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator(`[data-testid="checkbox-leg-win-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-leg-win-${raceIds[1]}"]`)).not.toBeChecked();

            await page.reload();
            await expect(page.locator(`[data-testid="checkbox-leg-win-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-leg-win-${raceIds[1]}"]`)).not.toBeChecked();
        });

        test('multi-race combos start with no default blank row until "+ Add combo" is clicked', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            await addRaces(page, eventUrl, 2);
            await gotoBetTypes(page, eventUrl);

            await expect(page.locator('[data-testid="empty-combos-daily_double"]')).toBeVisible();
            await expect(page.locator('[data-testid="combo-daily_double-new-0"]')).toHaveCount(0);

            await addCombo(page, 'daily_double');
            await expect(page.locator('[data-testid="combo-daily_double-new-0"]')).toBeVisible();
            await expect(page.locator('[data-testid="empty-combos-daily_double"]')).toBeHidden();
        });

        test('can create a multi-race combo and it persists as instance 0', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 2);
            await gotoBetTypes(page, eventUrl);

            const index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[1]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');

            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[1]}"]`)).toBeChecked();

            await page.reload();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[1]}"]`)).toBeChecked();
        });

        test('can add a second combo of the same multi-race bet type', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 4);
            await gotoBetTypes(page, eventUrl);

            // First Daily Double: races 1-2.
            let index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[1]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            // Second Daily Double: races 3-4, via a fresh combo row.
            index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[2]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[3]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            // Both combos now exist as separate instances (0 and 1).
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[1]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-1-${raceIds[2]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-1-${raceIds[3]}"]`)).toBeChecked();
        });

        test('"+ Add combo" lets two combos be configured and saved in one submission', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 4);
            await gotoBetTypes(page, eventUrl);

            // First row: races 1-2.
            let index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[1]}"]`);

            // Reveal a second row without saving yet.
            index = await addCombo(page, 'daily_double');
            await expect(page.locator(`[data-testid="combo-daily_double-new-${index}"]`)).toBeVisible();

            // Second row: races 3-4.
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[2]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[3]}"]`);

            // One save persists both combos.
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[1]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-1-${raceIds[2]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-1-${raceIds[3]}"]`)).toBeChecked();
        });

        test('Remove deletes a freshly-added combo row before saving', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 2);
            await gotoBetTypes(page, eventUrl);

            const index = await addCombo(page, 'daily_double');
            await expect(page.locator(`[data-testid="combo-daily_double-new-${index}"]`)).toBeVisible();

            await page.click(`[data-testid="btn-remove-combo-daily_double-new-${index}"]`);
            await expect(page.locator(`[data-testid="combo-daily_double-new-${index}"]`)).toHaveCount(0);
            await expect(page.locator('[data-testid="empty-combos-daily_double"]')).toBeVisible();

            // Nothing to persist - saving leaves the bet type unconfigured.
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[0]}"]`)).toHaveCount(0);
        });

        test('Remove deletes an already-persisted combo row', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 4);
            await gotoBetTypes(page, eventUrl);

            // Persist two combos first.
            let index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[1]}"]`);
            index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[2]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[3]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            // Remove the first persisted combo (instance 0), keep the second.
            await page.click('[data-testid="btn-remove-combo-daily_double-0"]');
            await expect(page.locator('[data-testid="combo-daily_double-0"]')).toHaveCount(0);
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            // Only the remaining combo persists, renumbered to instance 0.
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[2]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[3]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-1-${raceIds[0]}"]`)).toHaveCount(0);
        });

        test('two combos of the same multi-race bet type can share a race (overlapping legs)', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 3);
            await gotoBetTypes(page, eventUrl);

            // Combo 1: race 1 + race 2.
            let index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[1]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            // Combo 2: race 2 + race 3 - shares race 2 with combo 1. Not blocked by
            // a "race already used" restriction; each combo is validated independently.
            index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[1]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[2]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-0-${raceIds[1]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-1-${raceIds[1]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-daily_double-1-${raceIds[2]}"]`)).toBeChecked();
        });

        test('rejects a non-consecutive Daily Double combo with a validation error', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 3);
            await gotoBetTypes(page, eventUrl);

            // Daily Double requires 2 CONSECUTIVE races - race 1 + race 3 skips race 2.
            const index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[2]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');

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

        test('WTA accepts a non-consecutive free-form combo', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 3);
            await gotoBetTypes(page, eventUrl);

            // WTA has no fixed pattern - race 1 + race 3 (skipping race 2) is valid.
            const index = await addCombo(page, 'wta');
            await page.check(`[data-testid="checkbox-combo-wta-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-wta-new-${index}-${raceIds[2]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');

            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
            await expect(page.locator(`[data-testid="checkbox-combo-wta-0-${raceIds[0]}"]`)).toBeChecked();
            await expect(page.locator(`[data-testid="checkbox-combo-wta-0-${raceIds[2]}"]`)).toBeChecked();
        });

        test('bet types become read-only once the event is published', async ({ page }) => {
            const eventUrl = await createEventShowUrl(page);
            const raceIds = await addRaces(page, eventUrl, 2);
            await gotoBetTypes(page, eventUrl);

            await page.check(`[data-testid="checkbox-leg-win-${raceIds[0]}"]`);
            const index = await addCombo(page, 'daily_double');
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[0]}"]`);
            await page.check(`[data-testid="checkbox-combo-daily_double-new-${index}-${raceIds[1]}"]`);
            await page.click('[data-testid="btn-save-bet-types"]');
            await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();

            page.once('dialog', dialog => dialog.accept());
            await page.click('[data-testid="btn-publish-event"]');
            await expect(page).toHaveURL(`${BASE_URL}${eventUrl}`);

            await gotoBetTypes(page, eventUrl);
            await expect(page.locator('[data-testid="bet-types-form"]')).toHaveCount(0);
            await expect(page.locator(`[data-testid="checkbox-leg-win-${raceIds[0]}"]`)).toHaveCount(0);
            await expect(page.locator('[data-testid="btn-save-bet-types"]')).toHaveCount(0);
            // Read-only race labels are just the bare race number now (no "Race" prefix).
            await expect(page.locator('[data-testid="bet-type-row-win"]')).toContainText('1');
            await expect(page.locator('[data-testid="bet-type-row-daily_double"]')).toContainText('1');
            await expect(page.locator('[data-testid="bet-type-row-daily_double"]')).toContainText('2');
        });

    });

});
