import { test, expect, type Page } from '@playwright/test';
import {
    BASE_URL,
    ADMIN,
    AUDITOR,
    loginAs,
    registerAndLogin,
    ensureLiveRacingEnabled,
    createArena,
    createEventShowUrl,
    addRace,
    addHorses,
    publishEvent,
    adjustWalletAsAuditor,
    readWalletBalance,
} from '../helpers';

// Spin & Win is a floating prize wheel offered only on the Live Racing event
// pages. The server decides every spin, so the specs pin the outcome through
// WFBO ▸ Spin & Win ▸ Settings: house win rate 100% always lands on BANKRUPT;
// 0% with a single outcome weighted always lands on that outcome (x2 pays
// 2x the ₱5 spin cost, TRY AGAIN returns the bet).

type Weights = [string, string, string, string, string]; // try again, x2, x3, x10, x20
type SpinWinConfig = { enabled: boolean; hwr: string; weights?: Weights };

const DEFAULT_WEIGHTS: Weights = ['300', '150', '60', '10', '5'];

async function configureSpinWin(page: Page, cfg: SpinWinConfig): Promise<void> {
    await loginAs(page, ADMIN.mobile, ADMIN.password);
    await page.goto(`${BASE_URL}/wfbo/spin-win/settings`, { waitUntil: 'domcontentloaded' });
    await page.locator('[data-testid="toggle-sw-enabled"]').setChecked(cfg.enabled);
    await page.fill('[data-testid="input-sw-house-win-rate"]', cfg.hwr);
    const weights = cfg.weights ?? DEFAULT_WEIGHTS;
    for (const [i, tier] of ['try-again', 'small', 'medium', 'big', 'jackpot'].entries()) {
        await page.fill(`[data-testid="input-sw-weight-${tier}"]`, weights[i]);
    }
    await page.click('[data-testid="btn-save-sw-settings"]');
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

/** Registers a player funded with ₱100 and leaves them logged in. */
async function fundedPlayer(page: Page): Promise<void> {
    const player = await registerAndLogin(page);
    await loginAs(page, AUDITOR.mobile, AUDITOR.password);
    await adjustWalletAsAuditor(page, player.mobile, '100', 'Spin & Win e2e');
    await loginAs(page, player.mobile, player.password);
}

async function spinOnce(page: Page): Promise<void> {
    await page.click('[data-testid="spin-win-open"]');
    await expect(page.locator('[data-testid="spin-win-popup"]')).toBeVisible();
    await page.click('[data-testid="spin-win-spin"]');
    // The wheel animates for 5s before the result shows.
    await expect(page.locator('[data-testid="spin-win-result"]')).toHaveClass(/sw-visible/, { timeout: 15_000 });
}

test.describe.serial('Spin & Win — Live Racing prize wheel', () => {
    test.setTimeout(180_000);

    let eventPath = '';

    test.beforeAll(async ({ browser }) => {
        const page = await browser.newPage();
        await ensureLiveRacingEnabled(page);
        const arena = await createArena(page, 'SpinWin', { win: '10' });
        const { eventUrl, eventId } = await createEventShowUrl(page, arena);
        const race = await addRace(page, eventUrl, 1);
        await addHorses(page, race.raceUrl, 2);
        await publishEvent(page, eventUrl);
        eventPath = `/play/live-racing/${eventId}`;
        await page.close();
    });

    test.afterAll(async ({ browser }) => {
        const page = await browser.newPage();
        await configureSpinWin(page, { enabled: false, hwr: '' });
        await page.close();
    });

    test('a BANKRUPT spin takes the spin cost from the wallet and writes a ledger row', async ({ page }) => {
        await configureSpinWin(page, { enabled: true, hwr: '100' });
        await fundedPlayer(page);

        await page.goto(`${BASE_URL}${eventPath}`);
        await expect(page.locator('[data-testid="spin-win-launcher"]')).toBeVisible();

        await spinOnce(page);
        await expect(page.locator('[data-testid="spin-win-result"]')).toContainText('Bankrupt');
        await expect(page.locator('[data-testid="spin-win-balance"]')).toHaveText('₱95.00');
        expect(await readWalletBalance(page)).toBe(95);

        // A second spin - also books the ₱10 of house revenue the x2 win below
        // needs to get past the negative revenue guard.
        await page.click('[data-testid="spin-win-spin"]');
        await expect(page.locator('[data-testid="spin-win-balance"]')).toHaveText('₱90.00', { timeout: 15_000 });

        await page.goto(`${BASE_URL}/transactions`);
        const betRow = page.locator('[data-testid^="transaction-row-"]', { hasText: 'Spin & Win bet' });
        await expect(betRow).toHaveCount(2);
        await expect(betRow.first()).toContainText('-₱5.00');
        await expect(page.locator('[data-testid^="transaction-row-"]', { hasText: 'Spin & Win win' })).toHaveCount(0);
    });

    test('an x2 spin pays twice the spin cost into the wallet', async ({ page }) => {
        await configureSpinWin(page, { enabled: true, hwr: '0', weights: ['0', '1', '0', '0', '0'] });
        await fundedPlayer(page);

        await page.goto(`${BASE_URL}${eventPath}`);
        await expect(page.locator('[data-testid="spin-win-popup"]')).toContainText('win up to x20');
        await spinOnce(page);
        await expect(page.locator('[data-testid="spin-win-result"]')).toContainText('You won ₱10.00 (x2)');
        // ₱100 − ₱5 spin + ₱10 (x2).
        await expect(page.locator('[data-testid="spin-win-balance"]')).toHaveText('₱105.00');

        await page.goto(`${BASE_URL}/transactions`);
        const winRow = page.locator('[data-testid^="transaction-row-"]', { hasText: 'Spin & Win win' });
        await expect(winRow).toHaveCount(1);
        await expect(winRow).toContainText('₱10.00');
        await expect(winRow).toContainText('x2');
    });

    test('TRY AGAIN returns the bet', async ({ page }) => {
        await configureSpinWin(page, { enabled: true, hwr: '0', weights: ['1', '0', '0', '0', '0'] });
        await fundedPlayer(page);

        await page.goto(`${BASE_URL}${eventPath}`);
        await spinOnce(page);
        await expect(page.locator('[data-testid="spin-win-result"]')).toContainText('Try again — bet returned');
        await expect(page.locator('[data-testid="spin-win-balance"]')).toHaveText('₱100.00');

        await page.goto(`${BASE_URL}/transactions`);
        await expect(page.locator('[data-testid^="transaction-row-"]', { hasText: 'Spin & Win bet' })).toHaveCount(1);
        const refundRow = page.locator('[data-testid^="transaction-row-"]', { hasText: 'Spin & Win try again' });
        await expect(refundRow).toHaveCount(1);
        await expect(refundRow).toContainText('₱5.00');
        await expect(page.locator('[data-testid^="transaction-row-"]', { hasText: 'Spin & Win win' })).toHaveCount(0);
    });

    test('hiding the launcher leaves an edge tab that brings it back', async ({ page }) => {
        await configureSpinWin(page, { enabled: true, hwr: '100' });
        await fundedPlayer(page);

        await page.goto(`${BASE_URL}${eventPath}`);
        const launcher = page.locator('[data-testid="spin-win-launcher"]');
        const tab      = page.locator('[data-testid="spin-win-tab"]');

        await page.click('[data-testid="spin-win-hide"]');
        await expect(launcher).toHaveClass(/sw-hidden/);
        await expect(tab).toHaveClass(/sw-tab-show/);

        // Stays hidden across reloads.
        await page.reload();
        await expect(launcher).toHaveClass(/sw-hidden/);

        await tab.click();
        await expect(launcher).not.toHaveClass(/sw-hidden/);
    });

    test('the wheel is only on Live Racing, and gone when disabled', async ({ page }) => {
        await configureSpinWin(page, { enabled: true, hwr: '100' });
        await fundedPlayer(page);

        await page.goto(`${BASE_URL}/`);
        await expect(page.locator('[data-testid="spin-win-launcher"]')).toHaveCount(0);

        await configureSpinWin(page, { enabled: false, hwr: '100' });
        await fundedPlayer(page);
        await page.goto(`${BASE_URL}${eventPath}`);
        await expect(page.locator('[data-testid="wallet-bar"]')).toBeVisible();
        await expect(page.locator('[data-testid="spin-win-launcher"]')).toHaveCount(0);
    });
});
