import { test, expect, Page } from '@playwright/test';
import {
    BASE_URL,
    USER,
    ADMIN,
    loginAs,
    createArena,
    createEventShowUrl,
    addRace,
    addHorse,
    offerSingleBetTypes,
    publishEvent,
    ensureLiveRacingEnabled,
    closeBetting,
    saveResult,
    settleAndMarkRace,
    placeWizardBet,
    readWalletBalance,
} from '../helpers';

// Resolves an EventBetType id from a dividend row on the race's result form
// (only present once the race is closed). Mirrors the same helper in
// tests/acceptance_test/wfbo/live_racing_flow.spec.ts.
async function dividendRowId(page: Page, label: string | RegExp): Promise<string> {
    const rows  = page.locator('[data-testid^="dividend-row-"]');
    const count = await rows.count();
    const hits: string[] = [];
    for (let i = 0; i < count; i++) {
        const row  = rows.nth(i);
        const text = (await row.locator('label.form-hint').first().textContent())
            ?.replace(/\s+/g, ' ')
            .replace(/\s*\(whole combo\)\s*$/i, '')
            .trim() ?? '';
        const hit = typeof label === 'string' ? text === label : label.test(text);
        if (hit) {
            const testId = await row.getAttribute('data-testid');
            hits.push(testId?.replace('dividend-row-', '') ?? '');
        }
    }
    if (hits.length !== 1) {
        throw new Error(`Expected exactly one dividend row for ${label}, found ${hits.length}`);
    }
    return hits[0];
}

/**
 * Builds a published event with one race, two horses, a Win bet type
 * offered, a USER bet placed on the eventual winner, then settles it as a
 * winning bet with a known dividend. Leaves the browser logged in as ADMIN.
 */
async function setupSettledEventWithWinningBet(page: Page): Promise<{ arenaName: string; eventUrl: string; eventId: string; raceId: string; raceNumber: number; horseIds: string[]; dividend: string }> {
    await loginAs(page, ADMIN.mobile, ADMIN.password);

    const arenaName        = await createArena(page, 'LRResults', { win: '50.00' });
    const { eventUrl, eventId } = await createEventShowUrl(page, arenaName);
    const raceNumber = Date.now() % 1000;
    const { raceUrl, raceId }   = await addRace(page, eventUrl, raceNumber);
    const horseIds = [await addHorse(page, raceUrl, 1), await addHorse(page, raceUrl, 2)];

    await offerSingleBetTypes(page, eventUrl, [{ key: 'win', raceId }]);
    await publishEvent(page, eventUrl);
    await ensureLiveRacingEnabled(page);

    await loginAs(page, USER.mobile, USER.password);
    await placeWizardBet(page, {
        eventId,
        raceId,
        cardLabel: 'Win',
        positions: { 1: [horseIds[0]] },
    });

    await loginAs(page, ADMIN.mobile, ADMIN.password);
    await closeBetting(page, raceUrl);

    await page.goto(`${BASE_URL}${raceUrl}`);
    const winId    = await dividendRowId(page, 'Win');
    const dividend = '5.00';

    await saveResult(page, raceUrl, {
        finishers: [{ position: 1, horseId: horseIds[0] }],
        dividends: { [winId]: dividend },
    });

    return { arenaName, eventUrl, eventId, raceId, raceNumber, horseIds, dividend };
}

test.describe('Live Racing — Player Results Page', () => {

    test('unauthenticated access redirects to /login', async ({ page }) => {
        await page.goto(`${BASE_URL}/play/live-racing/results`);
        await expect(page).toHaveURL(new RegExp('/login'));
    });

    test('the "Select Event" dropdown switches the shown event', async ({ page }) => {
        // Event A — published, has a race with a result (from the shared helper).
        const { eventId: eventIdA } = await setupSettledEventWithWinningBet(page); // leaves browser as ADMIN

        // Event B — a second published event.
        const arenaB = await createArena(page, 'LRSwitch', { win: '50.00' });
        const { eventUrl: urlB, eventId: eventIdB } = await createEventShowUrl(page, arenaB);
        await publishEvent(page, urlB);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/live-racing/results`);

        const select = page.locator('[data-testid="select-event"]');
        // Both events are selectable.
        await expect(select.locator(`option[value="${eventIdA}"]`)).toHaveCount(1);
        await expect(select.locator(`option[value="${eventIdB}"]`)).toHaveCount(1);

        // Pick A → the page navigates and stays on A.
        await Promise.all([
            page.waitForURL(new RegExp(`event_id=${eventIdA}`)),
            select.selectOption(eventIdA),
        ]);
        await expect(select).toHaveValue(eventIdA);

        // Pick B → switches to B.
        await Promise.all([
            page.waitForURL(new RegExp(`event_id=${eventIdB}`)),
            page.locator('[data-testid="select-event"]').selectOption(eventIdB),
        ]);
        await expect(page.locator('[data-testid="select-event"]')).toHaveValue(eventIdB);
    });

    test('an event whose races have a result but are not settled shows the "not final yet" notice, not "no races"', async ({ page }) => {
        // setupSettledEventWithWinningBet records a result + grades bets but does
        // NOT run the Settlements-page mark-settled step, so the race is not
        // EventRace::isSettled() yet.
        const { eventId } = await setupSettledEventWithWinningBet(page);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/live-racing/results?event_id=${eventId}`);

        await expect(page.locator('[data-testid="results-not-finalized"]')).toBeVisible();
        await expect(page.locator('[data-testid="results-no-races"]')).toHaveCount(0);
        await expect(page.locator('[data-testid="results-race-tabs"]')).toHaveCount(0);
    });

    test('once a race is settled it appears on the results page with its finish order', async ({ page }) => {
        const { eventId, raceId, raceNumber } = await setupSettledEventWithWinningBet(page);

        await settleAndMarkRace(page, raceId);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/live-racing/results?event_id=${eventId}`);

        await expect(page.locator('[data-testid="results-not-finalized"]')).toHaveCount(0);
        await expect(page.locator('[data-testid="results-race-tabs"]')).toBeVisible();
        await expect(page.locator(`[data-testid="tab-race-${raceId}"]`)).toContainText(`Race ${raceNumber}`);
    });

    test('a settled win is recorded on the player Transactions page', async ({ page }) => {
        const { raceId } = await setupSettledEventWithWinningBet(page);

        await settleAndMarkRace(page, raceId);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/transactions`);

        const winRow = page.locator('[data-testid="transactions-table"] tbody tr').filter({ hasText: 'Live Racing win' }).first();
        await expect(winRow).toBeVisible();
        await expect(winRow).toContainText('Win');
        await expect(winRow).toContainText('+₱');
    });

    test('"Settle Only" credits the win but writes no Transactions row', async ({ page }) => {
        const winRows = () =>
            page.locator('[data-testid="transactions-table"] tbody tr').filter({ hasText: 'Live Racing win' });

        const { raceId } = await setupSettledEventWithWinningBet(page);

        // Baseline for this shared USER account (other tests in this file may
        // have already produced win rows for the same user).
        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/transactions`);
        const winRowsBefore = await winRows().count();
        await page.goto(`${BASE_URL}/play/live-racing/history`);
        const balanceBefore = await readWalletBalance(page);

        await settleAndMarkRace(page, raceId, /* withReceipt */ false);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/transactions`);
        // Settle Only added no new "Live Racing win" row...
        await expect(winRows()).toHaveCount(winRowsBefore);

        // ...but the wallet was still credited.
        await page.goto(`${BASE_URL}/play/live-racing/history`);
        expect(await readWalletBalance(page)).toBeGreaterThan(balanceBefore);
    });

    test('a settled win shows a "credited to your wallet" receipt block on My Bets', async ({ page }) => {
        const { raceId } = await setupSettledEventWithWinningBet(page);

        await settleAndMarkRace(page, raceId);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/live-racing/history`);

        const credit = page.locator('[data-testid^="ticket-credit-"]').first();
        await expect(credit).toBeVisible();
        await expect(credit).toContainText('credited to your wallet');
        await expect(credit).toContainText('Balance ₱');
        await expect(credit).toContainText('→ ₱');
    });

    test('the event detail page shows finishing order, dividend, and the player\'s own bet', async ({ page }) => {
        const { eventId, raceId, raceNumber, dividend } = await setupSettledEventWithWinningBet(page);

        await page.goto(`${BASE_URL}/play/live-racing/results/${eventId}`);

        const raceCard = page.locator(`[data-testid="player-race-result-${raceId}"]`);
        await expect(raceCard).toBeVisible();
        await expect(raceCard.locator(`[data-testid="player-race-result-text-${raceId}"]`)).toContainText('1. Horse #1');
        await expect(raceCard.locator(`[data-testid="player-dividends-summary-${raceId}"]`)).toContainText(dividend);

        const ticketsTable = page.locator('[data-testid="player-my-tickets-table"]');
        await expect(ticketsTable).toBeVisible();
        await expect(ticketsTable).toContainText('Win');
        await expect(ticketsTable.locator('.badge')).toContainText('Won');
        await expect(ticketsTable.locator('[data-testid^="race-number-"]')).toContainText(`R${raceNumber}`);
    });

    test('a result is publicly browsable even for a player who never bet on it', async ({ page, browser }) => {
        // Build the event/result as one browser context (ADMIN + betting USER)...
        const setupPage = await browser.newPage();
        const { eventId, raceId } = await setupSettledEventWithWinningBet(setupPage);
        await setupPage.close();

        // ...then view it from a completely separate, non-betting session.
        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/live-racing/results/${eventId}`);

        await expect(page.locator(`[data-testid="player-race-result-${raceId}"]`)).toBeVisible();
        await expect(page.locator('[data-testid="player-my-tickets-table"]')).toHaveCount(0);
    });

    test('the Race Results nav link is present on the history and event pages', async ({ page }) => {
        const { eventId } = await setupSettledEventWithWinningBet(page);

        await loginAs(page, USER.mobile, USER.password);

        await page.goto(`${BASE_URL}/play/live-racing/history`);
        await page.click('[data-testid="link-live-racing-results"]');
        await expect(page).toHaveURL(`${BASE_URL}/play/live-racing/results`);

        await page.goto(`${BASE_URL}/play/live-racing/${eventId}`);
        await page.click('[data-testid="link-live-racing-results"]');
        await expect(page).toHaveURL(`${BASE_URL}/play/live-racing/results`);
    });
});
