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

// Karera betting is admin-toggled (event.locked, race.bettingStatus, offering.status) rather
// than clock-driven like video-karera, so every bet-placement test here does its own WFBO
// setup first instead of relying on test.skip()-on-wrong-state.

function today(): string {
    return new Date().toISOString().slice(0, 10);
}

async function ensureKareraEnabled(page: Page): Promise<void> {
    await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
    await page.goto(`${BASE_URL}/wfbo/games/karera`, { waitUntil: 'domcontentloaded' });
    const status = page.locator('[data-testid="karera-enabled-status"]');
    if ((await status.textContent())?.trim() !== 'Enabled') {
        await page.click('[data-testid="toggle-karera-enabled"]');
        await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
    }
}

async function createArena(page: Page): Promise<string> {
    const arenaName = `Karera Front Test Arena ${Date.now()}`;
    await page.goto(`${BASE_URL}/wfbo/karera/arenas/new`);
    await page.fill('[data-testid="input-name"]', arenaName);
    await page.click('[data-testid="btn-submit"]');
    await expect(page).toHaveURL(`${BASE_URL}/wfbo/karera/arenas`);
    return arenaName;
}

/** Creates a today-dated event (so it's picked up by the player-facing "current event" query). */
async function createTodayEvent(page: Page, numberOfRaces: number): Promise<string> {
    const arenaName = await createArena(page);

    await page.goto(`${BASE_URL}/wfbo/karera/events/new`);
    await page.fill('[data-testid="input-event-date"]', today());
    await page.selectOption('[data-testid="select-arena"]', { label: arenaName });
    await page.fill('[data-testid="input-number-of-races"]', String(numberOfRaces));
    await page.click('[data-testid="btn-submit"]');

    await expect(page.locator('[data-testid="races-grid"]')).toBeVisible();
    return page.url();
}

/** Race cards render in raceNumber-ascending order, so index 0 == Race 1, etc. */
async function getOrderedRaceIds(page: Page): Promise<string[]> {
    const testIds = await page.locator('[data-testid^="race-card-"]').evaluateAll(
        (els) => els.map((el) => el.getAttribute('data-testid') ?? '')
    );
    return testIds.map((testId) => testId.replace('race-card-', ''));
}

async function addEntries(page: Page, eventUrl: string, raceIndex: number, count: number): Promise<void> {
    await page.goto(eventUrl);
    const raceCard = page.locator('[data-testid^="race-card-"]').nth(raceIndex);
    await raceCard.locator('[data-testid^="btn-actions-race-"]').click();
    await raceCard.locator('[data-testid^="btn-view-race-"]').click();

    for (let i = 0; i < count; i++) {
        await page.click('[data-testid="btn-add-entry"]');
        await expect(page.locator('[data-testid="entry-form"]')).toBeVisible();
        await page.fill('[data-testid="input-number"]', String(i + 1));
        await select2Fill(page, 'input-horse-name', `Horse ${Date.now()}-${raceIndex}-${i}`);
        await select2Fill(page, 'input-jockey-name', `Jockey ${Date.now()}-${raceIndex}-${i}`);
        await select2Fill(page, 'input-trainer-name', `Trainer ${Date.now()}-${raceIndex}-${i}`);
        await page.click('[data-testid="btn-submit"]');
        await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
    }

    await page.goto(eventUrl);
}

async function enableSingleRaceBetType(page: Page, eventUrl: string, raceId: string, code: string): Promise<void> {
    await page.goto(eventUrl);
    await page.click(`[data-testid="btn-enable-bet-type-${raceId}-${code}"]`);
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function createMultiRaceOffering(page: Page, eventUrl: string, betType: string, raceIds: string[]): Promise<void> {
    await page.goto(eventUrl);
    await page.click('[data-testid="btn-add-multi-race-bet"]');
    await expect(page.locator('[data-testid="multi-race-bet-form"]')).toBeVisible();
    await page.selectOption('[data-testid="select-multi-bet-type"]', { value: betType });
    for (const raceId of raceIds) {
        await page.check(`[data-testid="checkbox-race-${raceId}"]`);
    }
    await page.click('[data-testid="btn-submit-multi-race-bet"]');
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function scratchEntry(page: Page, eventUrl: string, raceIndex: number, entryId: string): Promise<void> {
    await page.goto(eventUrl);
    const raceCard = page.locator('[data-testid^="race-card-"]').nth(raceIndex);
    await raceCard.locator('[data-testid^="btn-actions-race-"]').click();
    await raceCard.locator('[data-testid^="btn-view-race-"]').click();

    page.once('dialog', dialog => dialog.accept());
    await page.click(`[data-testid="btn-scratch-${entryId}"]`);
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function closeRaceBetting(page: Page, eventUrl: string, raceIndex: number): Promise<void> {
    await page.goto(eventUrl);
    const raceCard = page.locator('[data-testid^="race-card-"]').nth(raceIndex);
    const statusSelect = raceCard.locator('[data-testid^="betting-status-"]');
    page.once('dialog', dialog => dialog.accept());
    await statusSelect.selectOption('closed');
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function topUpWalletForUser(page: Page, targetMobile: string, amount: number): Promise<void> {
    await loginAs(page, ADMIN.mobile, ADMIN.password);
    await page.goto(`${BASE_URL}/wfbo/users?search=${encodeURIComponent(targetMobile)}`);
    const userRow = page.locator('[data-testid^="user-row-"]').filter({ hasText: targetMobile });
    const rowId = (await userRow.first().getAttribute('data-testid'))?.replace('user-row-', '');
    await page.goto(`${BASE_URL}/wfbo/users/${rowId}/transactions`);
    await page.fill('[data-testid="adj-amount"]', String(amount));
    await page.fill('[data-testid="adj-reason"]', 'Karera bet acceptance test setup');
    await page.click('[data-testid="adj-submit"]');
    await expect(page.locator('[data-testid="flash-success"]')).toBeVisible();
}

async function getWalletBalance(page: Page): Promise<number> {
    await page.waitForTimeout(700); // balance-counter Stimulus controller animates in over 600ms
    const text = await page.locator('[data-testid="wallet-balance"]').textContent();
    return parseFloat((text ?? '0').replace(/[^\d.]/g, ''));
}

test.describe('Karera (real) — page load', () => {

    test('unauthenticated access redirects to /login', async ({ page }) => {
        await page.context().clearCookies();
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });
        expect(page.url()).toContain('/login');
    });

    test('game page loads for a logged-in user', async ({ page }) => {
        await ensureKareraEnabled(page);
        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });
        // Renders either the no-event empty state or the full race layout — both are valid
        // depending on whether another test has already set up today's event.
        const noEvent = page.locator('[data-testid="karera-no-event"]');
        const walletBar = page.locator('[data-testid="wallet-bar"]');
        const hasNoEvent = await noEvent.count() > 0;
        const hasWalletBar = await walletBar.count() > 0;
        expect(hasNoEvent || hasWalletBar).toBeTruthy();
    });

});

test.describe('Karera (real) — bet placement & wallet deduction', () => {

    test.beforeAll(async ({ browser }) => {
        const page = await browser.newPage();
        await ensureKareraEnabled(page);
        await topUpWalletForUser(page, USER.mobile, 1000);
        await page.close();
    });

    test('Win bet deducts wallet balance by the stake', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        const eventUrl = await createTodayEvent(page, 1);
        const [raceId] = await getOrderedRaceIds(page);
        await addEntries(page, eventUrl, 0, 1);
        await enableSingleRaceBetType(page, eventUrl, raceId, 'win');

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });

        const balanceBefore = await getWalletBalance(page);
        await expect(page.locator('[data-testid="karera-bet-tab-win"]')).toBeVisible();

        const form = page.locator('[data-testid="karera-bet-form-win"]');
        await form.locator('select[name="picks[]"]').selectOption({ index: 1 });
        await form.locator('[data-testid="karera-stake-input"]').fill('20');
        await form.locator('[data-testid="btn-place-bet"]').click();

        await page.waitForFunction(
            (before) => {
                const el = document.getElementById('wallet-bar-amount');
                if (!el) return false;
                const val = parseFloat((el.textContent ?? '0').replace(/[^\d.]/g, ''));
                return val < before;
            },
            balanceBefore,
            { timeout: 10000 },
        );
    });

    test('Trifecta requires three ordered picks and deducts the stake', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        const eventUrl = await createTodayEvent(page, 1);
        const [raceId] = await getOrderedRaceIds(page);
        await addEntries(page, eventUrl, 0, 3);
        await enableSingleRaceBetType(page, eventUrl, raceId, 'trifecta');

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });

        const balanceBefore = await getWalletBalance(page);
        await page.click('[data-testid="karera-bet-tab-trifecta"]');

        const form = page.locator('[data-testid="karera-bet-form-trifecta"]');
        const picks = form.locator('select[name="picks[]"]');
        await expect(picks).toHaveCount(3);
        await picks.nth(0).selectOption({ index: 1 });
        await picks.nth(1).selectOption({ index: 2 });
        await picks.nth(2).selectOption({ index: 3 });
        await form.locator('[data-testid="karera-stake-input"]').fill('10');
        await form.locator('[data-testid="btn-place-bet"]').click();

        await page.waitForFunction(
            (before) => {
                const el = document.getElementById('wallet-bar-amount');
                if (!el) return false;
                const val = parseFloat((el.textContent ?? '0').replace(/[^\d.]/g, ''));
                return val < before;
            },
            balanceBefore,
            { timeout: 10000 },
        );
    });

    test('Daily Double spans two races and deducts the stake', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        const eventUrl = await createTodayEvent(page, 2);
        const raceIds = await getOrderedRaceIds(page);
        await addEntries(page, eventUrl, 0, 1);
        await addEntries(page, eventUrl, 1, 1);
        await createMultiRaceOffering(page, eventUrl, 'daily_double', raceIds);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });

        const balanceBefore = await getWalletBalance(page);
        await expect(page.locator('[data-testid="karera-bet-tab-daily_double"]')).toBeVisible();
        await page.click('[data-testid="karera-bet-tab-daily_double"]');

        const form = page.locator('[data-testid="karera-bet-form-daily_double"]');
        const picks = form.locator('select[name="picks[]"]');
        await expect(picks).toHaveCount(2);
        await picks.nth(0).selectOption({ index: 1 });
        await picks.nth(1).selectOption({ index: 1 });
        await form.locator('[data-testid="karera-stake-input"]').fill('15');
        await form.locator('[data-testid="btn-place-bet"]').click();

        await page.waitForFunction(
            (before) => {
                const el = document.getElementById('wallet-bar-amount');
                if (!el) return false;
                const val = parseFloat((el.textContent ?? '0').replace(/[^\d.]/g, ''));
                return val < before;
            },
            balanceBefore,
            { timeout: 10000 },
        );
    });

    test('a multi-leg offering only shows its tab while viewing its starting-leg race', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        const eventUrl = await createTodayEvent(page, 5);
        const raceIds = await getOrderedRaceIds(page);
        for (let i = 0; i < 5; i++) {
            await addEntries(page, eventUrl, i, 1);
        }
        await createMultiRaceOffering(page, eventUrl, 'pick_5', raceIds);

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

        // Race 1 (the starting leg) shows the Pick 5 tab.
        await page.goto(`${BASE_URL}/play/karera?race=1`, { waitUntil: 'domcontentloaded' });
        await expect(page.locator('[data-testid="karera-bet-tab-pick_5"]')).toBeVisible();

        // Race 3 (a later leg, not the start) does not.
        await page.goto(`${BASE_URL}/play/karera?race=3`, { waitUntil: 'domcontentloaded' });
        await expect(page.locator('[data-testid="karera-bet-tab-pick_5"]')).toHaveCount(0);
    });

    test('scratched entries are excluded from pick options and rejected server-side', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        const eventUrl = await createTodayEvent(page, 1);
        const [raceId] = await getOrderedRaceIds(page);
        await addEntries(page, eventUrl, 0, 2);

        // Grab the scratched entry's id before scratching it.
        const raceCard = page.locator('[data-testid^="race-card-"]').first();
        await raceCard.locator('[data-testid^="btn-actions-race-"]').click();
        await raceCard.locator('[data-testid^="btn-view-race-"]').click();
        const entryRowTestIds = await page.locator('[data-testid^="entry-row-"]').evaluateAll(
            (els) => els.map((el) => el.getAttribute('data-testid') ?? '')
        );
        const scratchedEntryId = entryRowTestIds[0].replace('entry-row-', '');

        await scratchEntry(page, eventUrl, 0, scratchedEntryId);
        await enableSingleRaceBetType(page, eventUrl, raceId, 'win');

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });

        // Only the one remaining active entry is selectable (placeholder + 1 real option).
        const form = page.locator('[data-testid="karera-bet-form-win"]');
        const options = form.locator('select[name="picks[]"] option');
        await expect(options).toHaveCount(2);

        // Server-side guard: a raw request picking the scratched entry is rejected.
        const offeringRow = page.locator('[data-testid^="karera-bet-tab-win"]');
        await offeringRow.waitFor();
        const token = await form.locator('input[name="_token"]').inputValue();
        const offeringId = await form.locator('input[name="offering_id"]').inputValue();
        const response = await page.request.post(`${BASE_URL}/play/karera/bet`, {
            headers: { 'X-Requested-With': 'XMLHttpRequest' },
            form: {
                _token: token,
                offering_id: offeringId,
                'picks[]': scratchedEntryId,
                amount: '10',
            },
        });
        const body = await response.json();
        expect(body.error).toContain('scratched');
    });

    test('closing a race\'s betting status removes its offerings from the bet slip', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        const eventUrl = await createTodayEvent(page, 1);
        const [raceId] = await getOrderedRaceIds(page);
        await addEntries(page, eventUrl, 0, 1);
        await enableSingleRaceBetType(page, eventUrl, raceId, 'win');

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });
        await expect(page.locator('[data-testid="karera-bet-tab-win"]')).toBeVisible();

        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        await closeRaceBetting(page, eventUrl, 0);

        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera?race=1`, { waitUntil: 'domcontentloaded' });
        await expect(page.locator('[data-testid="karera-bet-tab-win"]')).toHaveCount(0);
        await expect(page.locator('[data-testid="betting-closed-panel"]')).toBeVisible();
    });

});

test.describe('Karera (real) — ticket history', () => {

    test('history page loads and reflects a placed bet', async ({ page }) => {
        await loginAs(page, OPERATIONS.mobile, OPERATIONS.password);
        const eventUrl = await createTodayEvent(page, 1);
        const [raceId] = await getOrderedRaceIds(page);
        await addEntries(page, eventUrl, 0, 1);
        await enableSingleRaceBetType(page, eventUrl, raceId, 'win');

        await topUpWalletForUser(page, USER.mobile, 200);
        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera`, { waitUntil: 'domcontentloaded' });

        const form = page.locator('[data-testid="karera-bet-form-win"]');
        await form.locator('select[name="picks[]"]').selectOption({ index: 1 });
        await form.locator('[data-testid="karera-stake-input"]').fill('5');
        await form.locator('[data-testid="btn-place-bet"]').click();
        await page.waitForTimeout(1500);

        await page.goto(`${BASE_URL}/play/karera/history`, { waitUntil: 'domcontentloaded' });
        await expect(page.locator('[data-testid="karera-history-title"]')).toBeVisible();
        const table = page.locator('[data-testid="karera-history-table"]');
        const empty = page.locator('[data-testid="karera-history-empty"]');
        const hasTable = await table.count() > 0;
        const hasEmpty = await empty.count() > 0;
        expect(hasTable || hasEmpty).toBeTruthy();
    });

    test('history back link returns to the game page', async ({ page }) => {
        await loginAs(page, USER.mobile, USER.password);
        await page.goto(`${BASE_URL}/play/karera/history`, { waitUntil: 'domcontentloaded' });
        await page.click('[data-testid="karera-history-back-link"]');
        await expect(page).toHaveURL(`${BASE_URL}/play/karera`);
    });

});
