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

// Setup helpers mirror tests/acceptance_test/front/live_racing.spec.ts (player
// side) and tests/acceptance_test/wfbo/event_race_dividends.spec.ts (result +
// dividend entry), combined here since this spec exercises the full
// place-bet -> save-result -> settle flow end to end.

async function createArena(page: Page, betTypeKey: string, amount: string): Promise<string> {
    const name = `Settle Arena ${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
    const fieldTestId = 'input-' + betTypeKey.replace(/_/g, '-') + '-amount';

    await page.goto(`${BASE_URL}/wfbo/managements/new`);
    await page.fill('[data-testid="input-name"]', name);
    await page.fill(`[data-testid="${fieldTestId}"]`, amount);
    await page.click('[data-testid="btn-submit"]');
    await expect(page).toHaveURL(`${BASE_URL}/wfbo/managements`);

    return name;
}

async function createEventShowUrl(page: Page, arenaName: string): Promise<string> {
    const dateStr = uniqueFutureDate();
    await page.goto(`${BASE_URL}/wfbo/events/new`);
    await page.selectOption('[data-testid="select-event-management"]', { label: arenaName });
    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 addRace(page: Page, eventUrl: string, raceNumber: number): Promise<string> {
    await page.goto(`${BASE_URL}${eventUrl}`);
    await page.click('[data-testid="btn-new-race"]');
    await page.fill('[data-testid="input-race-number"]', String(raceNumber));
    await page.click('[data-testid="btn-submit"]');

    const row = page.locator('[data-testid^="race-row-"]').filter({ hasText: String(raceNumber) });
    const raceHref = await row.locator('[data-testid^="btn-view-race-"]').getAttribute('href');
    return raceHref ?? '';
}

async function addHorse(page: Page, raceUrl: string, horseNumber: number): Promise<string> {
    await page.goto(`${BASE_URL}${raceUrl}`);
    await page.click('[data-testid="btn-new-horse"]');
    await page.fill('[data-testid="input-horse-number"]', String(horseNumber));
    await page.fill('[data-testid="input-horse-name"]', `Horse ${horseNumber}`);
    await page.fill('[data-testid="input-horse-jockey"]', `Jockey ${horseNumber}`);
    await page.click('[data-testid="btn-submit"]');

    const row = page.locator('[data-testid^="horse-row-"]').last();
    const horseId = (await row.getAttribute('data-testid'))?.replace('horse-row-', '');
    return horseId ?? '';
}

async function offerSingleBetType(page: Page, eventUrl: string, key: string, raceId: string): Promise<void> {
    await page.goto(`${BASE_URL}${eventUrl}?tab=bet-types`);
    await page.check(`[data-testid="checkbox-leg-${key}-${raceId}"]`);
    await page.click('[data-testid="btn-save-bet-types"]');
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function offerMultiBetType(page: Page, eventUrl: string, key: string, raceIds: string[]): Promise<void> {
    await page.goto(`${BASE_URL}${eventUrl}?tab=bet-types`);
    await page.click(`[data-testid="btn-add-combo-${key}"]`);
    for (const raceId of raceIds) {
        await page.check(`[data-testid="checkbox-combo-${key}-new-0-${raceId}"]`);
    }
    await page.click('[data-testid="btn-save-bet-types"]');
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function publishEvent(page: Page, eventUrl: string): Promise<void> {
    await page.goto(`${BASE_URL}${eventUrl}`);
    page.once('dialog', dialog => dialog.accept());
    await page.click('[data-testid="btn-publish-event"]');
}

async function closeBetting(page: Page, raceUrl: string): Promise<void> {
    await page.goto(`${BASE_URL}${raceUrl}`);
    page.once('dialog', dialog => dialog.accept());
    await page.click('[data-testid="btn-close-betting"]');
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function dividendRowId(page: Page, label: string): Promise<string> {
    const row = page.locator('[data-testid^="dividend-row-"]').filter({ hasText: label });
    const testId = await row.getAttribute('data-testid');
    return testId?.replace('dividend-row-', '') ?? '';
}

async function betTypeCardId(page: Page, label: string): Promise<string> {
    const tab = page.locator('.wizard-key-tab').filter({ hasText: label });
    const key = await tab.getAttribute('data-key');
    const radio = page.locator(`.wizard-key-panel[data-key="${key}"] .wizard-bet-radio`).first();
    return (await radio.getAttribute('value')) ?? '';
}

async function wizardPickBet(page: Page, cardId: string): Promise<void> {
    const key = await page.locator(`[data-testid="bet-panel-${cardId}"]`).getAttribute('data-key');
    await page.click(`[data-testid="tab-bet-key-${key}"]`);
}

// A multi-race bet type's legs are all enabled together once its combo is
// picked, but only the active leg tab's horse list is visible - so checking
// a non-first leg's box still needs its tab clicked first.
async function wizardPickRaceTab(page: Page, raceId: string, cardId: string): Promise<void> {
    await page.click(`[data-testid="tab-race-${raceId}-bet-${cardId}"]`);
}

function walletAmount(text: string | null): number {
    return parseFloat((text ?? '').replace(/[^0-9.]/g, ''));
}

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

    test('a straight Win bet is paid out automatically when the result is saved', async ({ page }) => {
        await loginAs(page, ADMIN.mobile, ADMIN.password);

        const arenaName = await createArena(page, 'win', '10.00');
        const eventUrl  = await createEventShowUrl(page, arenaName);
        const eventId   = eventUrl.split('/').pop() ?? '';
        const raceUrl   = await addRace(page, eventUrl, 1);
        const raceId    = raceUrl.split('/').pop() ?? '';
        const horse1    = await addHorse(page, raceUrl, 1);
        const horse2    = await addHorse(page, raceUrl, 2);

        await offerSingleBetType(page, eventUrl, 'win', raceId);
        await publishEvent(page, eventUrl);
        await ensureLiveRacingEnabled(page);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/live-racing/${eventId}`);
        const balanceBefore = walletAmount(await page.locator('[data-testid="wallet-bar-amount"]').textContent());

        const cardId = await betTypeCardId(page, 'Win');
        await wizardPickBet(page, cardId);
        await page.check(`[data-testid="checkbox-pos-1-horse-${horse1}-bet-${cardId}"]`);
        await page.click('[data-testid="btn-wizard-step1-next"]');
        await page.fill('[data-testid="input-wizard-amount"]', '10.00');
        await page.click('[data-testid="btn-wizard-submit"]');
        await expect(page.locator('[data-testid="wizard-step-3"]')).toBeVisible();

        // Betting must close before a result can be entered.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await closeBetting(page, raceUrl);

        const winId = await dividendRowId(page, 'Win');
        await page.selectOption('[data-testid="select-finisher-1"]', horse1);
        await page.selectOption('[data-testid="select-finisher-2"]', horse2);
        await page.fill(`[data-testid="input-dividend-${winId}"]`, '5.00');
        page.once('dialog', dialog => dialog.accept());
        await page.click('[data-testid="btn-save-result"]');

        await expect(page.locator('[data-testid="flash-success"]')).toContainText('live racing bet(s) settled');
        await expect(page.locator('[data-testid="flash-success"]')).toContainText('1 won');

        await loginAs(page, USER.mobile, USER.password);
        await expect.poll(async () => {
            const text = await page.locator('[data-testid="wallet-bar-amount"]').textContent().catch(() => null);
            return walletAmount(text);
        }, { timeout: 15000 }).toBeCloseTo(balanceBefore - 10 + 5, 1); // -stake(10) +payout(5)

        await page.goto(`${BASE_URL}/play/live-racing/history`);
        const row = page.locator(`[data-testid^="live-racing-history-row-"][data-arena="${arenaName}"]`);
        await expect(row).toContainText('Won');
        await expect(row).toContainText('₱5.00');
    });

    test('the Settle Event button reports no pending bets when there is nothing to settle', async ({ page }) => {
        await loginAs(page, ADMIN.mobile, ADMIN.password);

        const arenaName = await createArena(page, 'win', '10.00');
        const eventUrl  = await createEventShowUrl(page, arenaName);
        await publishEvent(page, eventUrl);

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

        await expect(page.locator('[data-testid="flash-success"]')).toContainText('No live racing bets were pending settlement');
    });

    test('a multi-race bet type stays unsettled until its last leg result is entered', async ({ page }) => {
        await loginAs(page, ADMIN.mobile, ADMIN.password);

        const arenaName = await createArena(page, 'daily_double', '15.00');
        const eventUrl  = await createEventShowUrl(page, arenaName);
        const eventId   = eventUrl.split('/').pop() ?? '';
        const race1Url  = await addRace(page, eventUrl, 1);
        const race1Id   = race1Url.split('/').pop() ?? '';
        const horse1a   = await addHorse(page, race1Url, 1);
        await addHorse(page, race1Url, 2);
        const race2Url  = await addRace(page, eventUrl, 2);
        const race2Id   = race2Url.split('/').pop() ?? '';
        const horse2a   = await addHorse(page, race2Url, 1);
        await addHorse(page, race2Url, 2);

        await offerMultiBetType(page, eventUrl, 'daily_double', [race1Id, race2Id]);
        await publishEvent(page, eventUrl);
        await ensureLiveRacingEnabled(page);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/live-racing/${eventId}`);
        const balanceBefore = walletAmount(await page.locator('[data-testid="wallet-bar-amount"]').textContent());

        const cardId = await betTypeCardId(page, 'Daily Double');
        await wizardPickBet(page, cardId);
        // Race 1's leg tab is active by default; race 2's needs its tab
        // clicked before its horse list becomes visible/checkable.
        await page.check(`[data-testid="checkbox-leg-${race1Id}-horse-${horse1a}-bet-${cardId}"]`);
        await wizardPickRaceTab(page, race2Id, cardId);
        await page.check(`[data-testid="checkbox-leg-${race2Id}-horse-${horse2a}-bet-${cardId}"]`);
        await page.click('[data-testid="btn-wizard-step1-next"]');
        await page.fill('[data-testid="input-wizard-amount"]', '15.00');
        await page.click('[data-testid="btn-wizard-submit"]');
        await expect(page.locator('[data-testid="wizard-step-3"]')).toBeVisible();

        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await closeBetting(page, race1Url);
        await page.selectOption('[data-testid="select-finisher-1"]', horse1a);
        page.once('dialog', dialog => dialog.accept());
        await page.click('[data-testid="btn-save-result"]');
        // Race 1 alone isn't the combo's last leg - the pending bet is seen
        // but stays open (0 won/lost/void) until the last leg resolves it.
        await expect(page.locator('[data-testid="flash-success"]')).toContainText('(0 won, 0 lost, 0 void)');

        await closeBetting(page, race2Url);
        const comboId = await dividendRowId(page, 'Daily Double');
        await page.selectOption('[data-testid="select-finisher-1"]', horse2a);
        await page.fill(`[data-testid="input-dividend-${comboId}"]`, '40.00');
        page.once('dialog', dialog => dialog.accept());
        await page.click('[data-testid="btn-save-result"]');
        await expect(page.locator('[data-testid="flash-success"]')).toContainText('1 won');

        await loginAs(page, USER.mobile, USER.password);
        await expect.poll(async () => {
            const text = await page.locator('[data-testid="wallet-bar-amount"]').textContent().catch(() => null);
            return walletAmount(text);
        }, { timeout: 15000 }).toBeCloseTo(balanceBefore - 15 + 40, 1); // -stake(15) +payout(40)
    });

});
