import { test, expect, Page } from '@playwright/test';
import {
    BASE_URL,
    ADMIN,
    SUPER_ADMIN,
    AUDITOR,
    loginAs,
    registerAndLogin,
    ensureLiveRacingEnabled,
    createArena,
    createEventShowUrl,
    addRace,
    addHorses,
    offerSingleBetTypes,
    offerMultiBetTypeCombos,
    publishEvent,
    placeWizardBet,
    closeBetting,
    saveResult,
    settleAndMarkRace,
    adjustWalletAsAuditor,
    readUserWalletAsAdmin,
    walletAmount,
} from '../helpers';

// WFBO race ▸ Correct result: a super admin fixes a wrongly declared, already
// settled result. The wrong winner's payout is taken back and the real winner
// is paid.

async function comboBlockId(page: Page, label: RegExp): Promise<string> {
    const blocks = page.locator('[data-testid^="combo-bettype-"]');
    for (let i = 0; i < (await blocks.count()); i++) {
        const text = (await blocks.nth(i).locator('label.fw-semibold').first().textContent())
            ?.replace(/\s+/g, ' ').replace(/\s*\([\d-]+…\)\s*$/u, '').trim() ?? '';
        if (label.test(text)) {
            return (await blocks.nth(i).getAttribute('data-testid'))?.replace('combo-bettype-', '') ?? '';
        }
    }
    throw new Error(`no combination block for ${label}`);
}

test.describe('WFBO race — correct a settled result', () => {
    test.setTimeout(300_000);

    test('Daily Double declared with the wrong last leg is corrected after settlement', async ({ page }) => {
        const wrong = await registerAndLogin(page);
        const right = await registerAndLogin(page);
        const winner = await registerAndLogin(page); // Win on race 2 - must be left alone
        await loginAs(page, AUDITOR.mobile, AUDITOR.password);
        await adjustWalletAsAuditor(page, wrong.mobile, '100', 'Correct result e2e');
        await adjustWalletAsAuditor(page, right.mobile, '100', 'Correct result e2e');
        await adjustWalletAsAuditor(page, winner.mobile, '100', 'Correct result e2e');

        // Daily Double over races 1 -> 2 (plus Win on race 1 so it has a result).
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await ensureLiveRacingEnabled(page);
        const arena = await createArena(page, 'Correct', { win: '10', daily_double: '10' });
        const { eventUrl, eventId } = await createEventShowUrl(page, arena);
        const race1 = await addRace(page, eventUrl, 1);
        const race2 = await addRace(page, eventUrl, 2);
        const r1 = await addHorses(page, race1.raceUrl, 2);
        const r2 = await addHorses(page, race2.raceUrl, 2);
        await offerSingleBetTypes(page, eventUrl, [{ key: 'win', raceId: race1.raceId }, { key: 'win', raceId: race2.raceId }]);
        await offerMultiBetTypeCombos(page, eventUrl, 'daily_double', [[race1.raceId, race2.raceId]]);
        await publishEvent(page, eventUrl);

        // One player backs 1-2, the other 1-1, ₱10 each.
        await loginAs(page, wrong.mobile, wrong.password);
        await placeWizardBet(page, { eventId, cardLabel: /^Daily Double$/, legs: { [race1.raceId]: [r1[0]], [race2.raceId]: [r2[1]] }, amount: '10' });
        await loginAs(page, right.mobile, right.password);
        await placeWizardBet(page, { eventId, cardLabel: /^Daily Double$/, legs: { [race1.raceId]: [r1[0]], [race2.raceId]: [r2[0]] }, amount: '10' });
        await loginAs(page, winner.mobile, winner.password);
        await placeWizardBet(page, { eventId, raceId: race2.raceId, cardLabel: 'Win', positions: { 1: [r2[1]] }, amount: '10' });

        // Results: race 1 won by horse 1; the DD is wrongly declared 1-2 and settled.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await closeBetting(page, race1.raceUrl);
        await page.goto(`${BASE_URL}${race1.raceUrl}`);
        const winId = await comboBlockId(page, /^Win$/);
        await saveResult(page, race1.raceUrl, { combinations: { [winId]: [{ horseIds: [r1[0]], dividend: '20.00' }] } });
        await closeBetting(page, race2.raceUrl);
        await page.goto(`${BASE_URL}${race2.raceUrl}`);
        const ddId = await comboBlockId(page, /^Daily Double/);
        const win2Id = await comboBlockId(page, /^Win$/);
        await saveResult(page, race2.raceUrl, { combinations: {
            [ddId]:   [{ horseIds: [r1[0], r2[1]], dividend: '50.00' }],
            [win2Id]: [{ horseIds: [r2[1]], dividend: '30.00' }],
        } });
        await settleAndMarkRace(page, race1.raceId);
        await settleAndMarkRace(page, race2.raceId);

        const wrongPaid = await readUserWalletAsAdmin(page, wrong.mobile);
        expect(wrongPaid).toBeGreaterThan(90); // 100 - 10 stake + the wrong win
        expect(await readUserWalletAsAdmin(page, right.mobile)).toBe(90);
        const winnerPaid = await readUserWalletAsAdmin(page, winner.mobile);
        expect(winnerPaid).toBeGreaterThan(90);

        // A plain admin has no Correct result button.
        await page.goto(`${BASE_URL}${race2.raceUrl}`);
        await expect(page.locator('[data-testid="race-result-summary"]')).toBeVisible();
        await expect(page.locator('[data-testid="btn-correct-result"]')).toHaveCount(0);

        // Super admin corrects the DD to 1-1 at 25.00 and previews it.
        await loginAs(page, SUPER_ADMIN.mobile, SUPER_ADMIN.password);
        await page.goto(`${BASE_URL}${race2.raceUrl}`);
        await page.click('[data-testid="btn-correct-result"]');
        await expect(page).toHaveURL(/\/correct-result$/);
        const row = page.locator(`[data-testid="combo-bettype-${ddId}"] [data-combo-row]`).first();
        await expect(row.locator('select').nth(1)).toHaveValue(r2[1]); // prefilled with the saved 1-2
        await row.locator('select').nth(1).selectOption(r2[0]);
        await row.locator('input[type="number"]').fill('25.00');
        await page.click('[data-testid="btn-preview-correction"]');

        const preview = page.locator('[data-testid="correct-preview"]');
        await expect(preview).toContainText('2 tickets will change');
        await expect(page.locator('body')).toContainText("you don't need to settle the race again");
        await expect(page.locator('[data-testid="correct-preview-types"]')).toContainText('Only Daily Double tickets are affected');
        await expect(page.locator('[data-testid="correct-preview-table"] tbody tr')).toHaveCount(2);
        await expect(page.locator('[data-testid="correct-clawback"]')).toHaveText(`₱${(wrongPaid - 90).toFixed(2)}`);
        const newPayout = walletAmount(await page.locator('[data-testid="correct-new-payouts"]').textContent());
        expect(newPayout).toBeGreaterThan(0);

        // Nothing changed yet.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        expect(await readUserWalletAsAdmin(page, wrong.mobile)).toBe(wrongPaid);

        // Apply with a reason.
        await loginAs(page, SUPER_ADMIN.mobile, SUPER_ADMIN.password);
        await page.goto(`${BASE_URL}${race2.raceUrl}/correct-result`);
        const row2 = page.locator(`[data-testid="combo-bettype-${ddId}"] [data-combo-row]`).first();
        await row2.locator('select').nth(1).selectOption(r2[0]);
        await row2.locator('input[type="number"]').fill('25.00');
        await page.click('[data-testid="btn-preview-correction"]');
        await page.fill('[data-testid="input-correct-reason"]', 'DD declared 1-2, actual winner 1-1');
        page.once('dialog', dialog => dialog.accept());
        await page.click('[data-testid="btn-apply-correction"]');

        // The re-grade runs in batches from the page; no separate settlement step.
        const progress = page.locator('[data-testid="correct-progress"]');
        await expect(progress).toHaveAttribute('data-correction-state', 'done', { timeout: 30000 });
        await expect(page.locator('[data-testid="correct-progress-text"]')).toContainText('Result corrected. 2 tickets re-graded');
        await page.goto(`${BASE_URL}${race2.raceUrl}`);
        await expect(page.locator('[data-testid="dividends-summary"]')).toContainText('1-1 : 25.00');

        // The wrong win is taken back; the real winner is paid.
        await loginAs(page, ADMIN.mobile, ADMIN.password);
        expect(await readUserWalletAsAdmin(page, wrong.mobile)).toBe(90);
        expect(await readUserWalletAsAdmin(page, right.mobile)).toBeCloseTo(90 + newPayout, 2);
        // The Win bet type wasn't changed, so its winner is untouched.
        expect(await readUserWalletAsAdmin(page, winner.mobile)).toBe(winnerPaid);
    });
});
