import { test, expect, Page } from '@playwright/test';
import {
    BASE_URL, ADMIN, AGENT, loginAs,
    ensureLiveRacingEnabled, createArena, createEventShowUrl, addRace, addHorses,
    offerMultiBetTypeCombos, publishEvent, placeWizardBet, closeBetting, saveResult,
    registerUserWithReferralCode, adjustWalletAsAuditor, setLiveRacingCommissionRate,
    settleAndMarkRace,
} from '../helpers';

test.describe('WFBO Agent Commissions Tab', () => {

    /**
     * Iterates WFBO user rows and returns the URL for the first user whose
     * transactions page shows the Commissions tab (i.e. a Master Agent or Agent).
     */
    async function findAgentTransactionsUrl(page: Page): Promise<string | null> {
        await page.goto(`${BASE_URL}/wfbo/users`);
        const rows = page.locator('[data-testid^="user-row-"]');
        const count = await rows.count();

        const rowIds: string[] = [];
        for (let i = 0; i < count; i++) {
            const rowId = (await rows.nth(i).getAttribute('data-testid'))?.replace('user-row-', '');
            if (rowId) rowIds.push(rowId);
        }

        for (const rowId of rowIds) {
            const url = `${BASE_URL}/wfbo/users/${rowId}/transactions`;
            await page.goto(url);

            if (await page.locator('[data-testid="tab-commissions"]').isVisible()) {
                return url;
            }
        }
        return null;
    }

    /**
     * Returns the URL for the first user whose transactions page does NOT
     * show the Commissions tab (i.e. a plain player or staff user).
     */
    async function findNonAgentTransactionsUrl(page: Page): Promise<string | null> {
        await page.goto(`${BASE_URL}/wfbo/users`);
        const rows = page.locator('[data-testid^="user-row-"]');
        const count = await rows.count();

        const rowIds: string[] = [];
        for (let i = 0; i < count; i++) {
            const rowId = (await rows.nth(i).getAttribute('data-testid'))?.replace('user-row-', '');
            if (rowId) rowIds.push(rowId);
        }

        for (const rowId of rowIds) {
            const url = `${BASE_URL}/wfbo/users/${rowId}/transactions`;
            await page.goto(url);

            if (!await page.locator('[data-testid="tab-commissions"]').isVisible()) {
                return url;
            }
        }
        return null;
    }

    test.beforeEach(async ({ page }) => {
        await loginAs(page, ADMIN.mobile, ADMIN.password);
    });

    // ── Commissions tab visibility ─────────────────────────────────────────────

    test('commissions tab is visible for agent users', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url, 'No agent user found in fixture data').not.toBeNull();

        await expect(page.locator('[data-testid="tab-commissions"]')).toBeVisible();
    });

    test('commissions tab is not visible for non-agent users', async ({ page }) => {
        const url = await findNonAgentTransactionsUrl(page);
        expect(url, 'All fixture users appear to be agents').not.toBeNull();

        await expect(page.locator('[data-testid="tab-commissions"]')).not.toBeVisible();
    });

    // ── Commissions tab navigation ─────────────────────────────────────────────

    test('clicking commissions tab shows commissions table', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.click('[data-testid="tab-commissions"]');

        await expect(page.locator('[data-testid="user-commissions-table-card"]')).toBeVisible();
        await expect(page.locator('[data-testid="user-commissions-table"]')).toBeVisible();
    });

    test('commissions table shows expected column headers', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.click('[data-testid="tab-commissions"]');

        const table = page.locator('[data-testid="user-commissions-table"]');
        await expect(table.locator('th', { hasText: 'Player' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Mobile #' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Game' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Ref #' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Detail' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Bet Amount' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Commission' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Status' })).toBeVisible();
    });

    test('commissions tab has a date-range PDF export form', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.click('[data-testid="tab-commissions"]');

        await expect(page.locator('[data-testid="commissions-export-form"]')).toBeVisible();
        await expect(page.locator('[data-testid="commissions-export-from"]')).toBeVisible();
        await expect(page.locator('[data-testid="commissions-export-to"]')).toBeVisible();
        await expect(page.locator('[data-testid="btn-export-commissions"]')).toBeVisible();
    });

    test('exporting commissions downloads a PDF', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.goto(url + '?type=commissions');
        const downloadPromise = page.waitForEvent('download');
        await page.click('[data-testid="btn-export-commissions"]');
        const download = await downloadPromise;
        expect(download.suggestedFilename()).toMatch(/^commissions-.*\.pdf$/);
    });

    test('commissions tab shows count badge', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        const tab = page.locator('[data-testid="tab-commissions"]');
        await expect(tab.locator('.tab-count')).toBeVisible();
    });

    test('commissions table shows empty message when no commissions', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.click('[data-testid="tab-commissions"]');

        const table = page.locator('[data-testid="user-commissions-table"]');
        const rows = table.locator('tbody tr[data-testid^="commission-row-"]');
        const emptyCell = table.locator('td.td-empty');

        const rowCount = await rows.count();
        if (rowCount === 0) {
            await expect(emptyCell).toBeVisible();
            await expect(emptyCell).toContainText('No commissions found');
        } else {
            await expect(rows.first()).toBeVisible();
        }
    });

    // ── Commissions tab: event/race filter ─────────────────────────────────────

    test('commissions tab has an event/race filter; race select is disabled until an event is picked', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.goto(url + '?type=commissions');

        await expect(page.locator('[data-testid="commissions-filter-form"]')).toBeVisible();
        await expect(page.locator('[data-testid="commissions-filter-event"]')).toBeVisible();
        await expect(page.locator('[data-testid="commissions-filter-race"]')).toBeDisabled();
    });

    // ── "By Event / Race" tab ─────────────────────────────────────────────────

    test('"By Event / Race" tab prompts to pick an event before showing anything', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await expect(page.locator('[data-testid="tab-commissions-by-race"]')).toBeVisible();
        await page.click('[data-testid="tab-commissions-by-race"]');

        await expect(page.locator('[data-testid="commissions-by-race-filter-event"]')).toBeVisible();
        await expect(page.locator('[data-testid="commissions-by-race-empty-prompt"]')).toBeVisible();
        await expect(page.locator('[data-testid="commissions-by-race-table"]')).not.toBeAttached();
    });

    test('picking an event on "By Event / Race" shows its races with a total below', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.click('[data-testid="tab-commissions-by-race"]');

        const options = page.locator('[data-testid="commissions-by-race-filter-event"] option');
        if (await options.count() <= 1) {
            test.skip();
            return;
        }
        const eventId = await options.nth(1).getAttribute('value');
        await page.selectOption('[data-testid="commissions-by-race-filter-event"]', eventId!);

        const table = page.locator('[data-testid="commissions-by-race-table"]');
        await expect(table).toBeVisible();
        await expect(table.locator('th', { hasText: 'Race' })).toBeVisible();
        await expect(table.locator('th', { hasText: 'Commission' })).toBeVisible();
        await expect(page.locator(`[data-testid="cbr-event-total-${eventId}"]`)).toBeVisible();
    });

    test('"By Event / Race" tab is not visible for non-agent users', async ({ page }) => {
        const url = await findNonAgentTransactionsUrl(page);
        expect(url, 'All fixture users appear to be agents').not.toBeNull();

        await expect(page.locator('[data-testid="tab-commissions-by-race"]')).not.toBeVisible();
    });

    // ── Bets tab: no Commission column (commissions are on their own tab) ────────

    test('bets tab does not have a Commission column', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await page.goto(url + '?type=bets');

        const table = page.locator('[data-testid="user-bets-table"]');
        await expect(table.locator('th', { hasText: 'Commission' })).not.toBeVisible();
    });

    // ── Player Bets tab (agent downline bets) ───────────────────────────────────

    test('player bets tab is visible for agents and lists downline bets with a Player column', async ({ page }) => {
        const url = await findAgentTransactionsUrl(page);
        expect(url).not.toBeNull();

        await expect(page.locator('[data-testid="tab-player-bets"]')).toBeVisible();
        await page.click('[data-testid="tab-player-bets"]');

        const table = page.locator('[data-testid="user-player-bets-table"]');
        await expect(table).toBeVisible();
        await expect(table.locator('th', { hasText: 'Player' })).toBeVisible();
    });

    test('player bets tab is not visible for non-agent users', async ({ page }) => {
        const url = await findNonAgentTransactionsUrl(page);
        expect(url, 'All fixture users appear to be agents').not.toBeNull();

        await expect(page.locator('[data-testid="tab-player-bets"]')).not.toBeVisible();
    });

});

// ── "By Event / Race" tab: last-leg attribution ────────────────────────────

test.describe('WFBO Agent Commissions — last-leg attribution', () => {
    test.setTimeout(120_000);

    /** Resolve an EventBetType id from a combination-entry block on a race result form. */
    async function dividendRowId(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("a losing multi-leg bet's commission is attributed to the last leg's race only", async ({ page }) => {
        await setLiveRacingCommissionRate(page, '10');

        // Referred user with no master agent: referrer AGENT earns the full rate.
        const ru = await registerUserWithReferralCode(page, 'ABCDEF01');
        await adjustWalletAsAuditor(page, ru.mobile, '5000.00', 'Last-leg commission test funding');

        await loginAs(page, ADMIN.mobile, ADMIN.password);
        const arena   = await createArena(page, 'LRLastLeg', { daily_double: '10' });
        const created = await createEventShowUrl(page, arena);
        const race1   = await addRace(page, created.eventUrl, 1);
        const race2   = await addRace(page, created.eventUrl, 2);
        const h1 = await addHorses(page, race1.raceUrl, 2);
        const h2 = await addHorses(page, race2.raceUrl, 2);
        await offerMultiBetTypeCombos(page, created.eventUrl, 'daily_double', [[race1.raceId, race2.raceId]]);
        await publishEvent(page, created.eventUrl);
        await ensureLiveRacingEnabled(page);

        await loginAs(page, ru.mobile, ru.password);
        // Leg 1: the horse that will actually win. Leg 2: a horse that will
        // NOT win, so the combo loses and commission is generated.
        await placeWizardBet(page, {
            eventId: created.eventId, raceId: race1.raceId, cardLabel: /^Daily Double$/,
            legs: { [race1.raceId]: [h1[0]], [race2.raceId]: [h2[1]] },
        });

        await loginAs(page, ADMIN.mobile, ADMIN.password);
        await closeBetting(page, race1.raceUrl);
        await closeBetting(page, race2.raceUrl);
        // Leg 1 (not the last leg) has no bet type of its own to enter — an
        // empty result still records its finish order, which grading needs.
        await saveResult(page, race1.raceUrl, {});
        await page.goto(`${BASE_URL}${race2.raceUrl}`);
        const ddId = await dividendRowId(page, /^Daily Double$/);
        await saveResult(page, race2.raceUrl, {
            combinations: { [ddId]: [{ horseIds: [h1[0], h2[0]], dividend: '20.00' }] },
        });

        // A multi-leg bet grades once every leg has a result, so it's swept
        // by settling whichever leg's pending list still picks it up.
        await settleAndMarkRace(page, race2.raceId);

        await page.goto(`${BASE_URL}/wfbo/users?search=${encodeURIComponent(AGENT.mobile)}`);
        const row  = page.locator('[data-testid^="user-row-"]').filter({ hasText: AGENT.mobile });
        const href = await row.locator('[data-testid^="btn-view-transactions-"]').getAttribute('href');
        await page.goto(`${BASE_URL}${href ?? ''}?type=commissions-by-race&event=${created.eventId}`);

        // 10% x 10.00 stake = 1.00, attributed whole to race 2 (the last
        // leg) — race 1 (the first leg) gets no row at all.
        const race2Row = page.locator(`[data-testid="cbr-race-${created.eventId}-${race2.raceId}"]`);
        await expect(race2Row).toBeVisible();
        await expect(race2Row).toContainText('1.00');
        await expect(page.locator(`[data-testid="cbr-race-${created.eventId}-${race1.raceId}"]`)).toHaveCount(0);
    });
});
